Spring中@PostConstruct执行两次的原因及修复方案问询
Spring中@PostConstruct方法执行两次的问题修复
问题背景
在Spring Web应用中,通过@PostConstruct与@Bean实现启动初始化,但发现MyCache类的init()方法被执行了两次。
代码示例
public class MyCache<T> { @PostConstruct public void init() { System.out.println("inittttttttttt"); // 初始化逻辑 } } @Configuration @EnableScheduling public class AppConfig { @Bean public MyCache<MyData> myCache() { return new MyCache<MyData>(); } } public class TextFilter { private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); private MyCache<MyData> cache; public TextFilter() { this.context.scan("com.sensitive_words.utils"); this.context.refresh(); this.cache = this.context.getBean(MyCache.class); } public String filter(String originalText) { return this.cache.get().filter(originalText); } }
日志输出
2022-10-30 13:53:38.837 INFO 106994 --- [ main] y.c.s.SensitiveWordsApplication : Starting SensitiveWordsApplication using Java 11.0.16 on yves-Inspiron-5488 with PID 106994 (/home/yves/java_ws/sensitive_words/target/classes started by yves in /home/yves/java_ws/sensitive_words) 2022-10-30 13:53:38.839 INFO 106994 --- [ main] y.c.s.SensitiveWordsApplication : No active profile set, falling back to 1 default profile: "default" 2022-10-30 13:53:39.545 INFO 106994 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8090 (http) 2022-10-30 13:53:39.559 INFO 106994 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2022-10-30 13:53:39.560 INFO 106994 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.68] 2022-10-30 13:53:39.654 INFO 106994 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2022-10-30 13:53:39.654 INFO 106994 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 763 ms inittttttttttt 2022-10-30 13:53:39.769 INFO 106994 --- [ main] s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing inittttttttttt 2022-10-30 13:53:40.080 INFO 106994 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8090 (http) with context path '' 2022-10-30 13:53:40.096 INFO 106994 --- [ main] y.c.s.SensitiveWordsApplication : Started SensitiveWordsApplication in 1.64 seconds (JVM running for 2.341)
原因分析
核心问题是存在两个独立的Spring容器:
- Spring Web应用启动时会自动创建根容器(
ServletWebServerApplicationContext),扫描配置类并初始化MyCacheBean,触发第一次init()执行。 TextFilter构造函数中手动创建了AnnotationConfigApplicationContext,并扫描同一个包路径,导致第二次初始化MyCache实例,触发第二次init()执行。
修复方案
步骤1:重构TextFilter,使用Spring依赖注入
移除手动创建的容器实例,通过Spring的依赖注入获取MyCache,同时将TextFilter声明为Spring管理的Bean:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class TextFilter { private final MyCache<MyData> cache; @Autowired public TextFilter(MyCache<MyData> cache) { this.cache = cache; } public String filter(String originalText) { return this.cache.get().filter(originalText); } }
步骤2:确保TextFilter被Spring扫描到
保证TextFilter所在的包在Spring根容器的扫描范围内(比如与AppConfig同包,或通过@ComponentScan注解明确指定扫描路径)。
步骤3:通过Spring容器获取TextFilter实例
禁止手动new TextFilter(),需从Spring容器中获取实例:
// 在Spring管理的类中直接注入 @Autowired private TextFilter textFilter; // 或通过ApplicationContext获取 TextFilter textFilter = applicationContext.getBean(TextFilter.class);
通过以上操作,整个应用只会维护一个Spring容器,MyCache仅会被初始化一次,init()方法也只会执行一次。
内容的提问来源于stack exchange,提问作者Yves
相关产品推荐
相关产品推荐

