You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Spring/Spring Boot中如何让Bean在Tomcat初始化前完成实例化?

在Spring/Spring Boot中让Bean在Tomcat初始化前完成实例化的方案

要实现让耗时较长的核心Bean在Tomcat启动日志(如下所示)之前完成实例化,且初始化失败时直接终止应用、不启动Tomcat,可采用以下几种可靠方案:

o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
o.apache.catalina.core.StandardService   : Starting service [Tomcat]
org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.73]
o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 439 ms

方案1:手动预初始化独立上下文(最直接可控)

在Spring Boot应用启动前,手动创建一个独立的Spring上下文,专门初始化核心Bean。只有当Bean初始化成功后,再启动主应用。

代码示例

public static void main(String[] args) {
    // 1. 创建预初始化上下文,加载核心Bean
    AnnotationConfigApplicationContext preInitContext = new AnnotationConfigApplicationContext();
    preInitContext.register(CriticalLongInitBean.class); // 替换为你的核心Bean类

    try {
        preInitContext.refresh();
        // 验证Bean是否成功初始化(触发实例化和初始化逻辑)
        CriticalLongInitBean criticalBean = preInitContext.getBean(CriticalLongInitBean.class);
        System.out.println("核心Bean初始化成功");
    } catch (Exception e) {
        System.err.println("核心Bean初始化失败,终止应用启动");
        preInitContext.close();
        System.exit(1); // 直接终止JVM,避免启动Tomcat
    }

    // 2. 核心Bean初始化成功,启动Spring Boot主应用
    ConfigurableApplicationContext mainContext = SpringApplication.run(YourSpringBootApp.class, args);
    
    // 可选:将预初始化的Bean注册到主上下文,避免重复创建
    mainContext.getBeanFactory().registerSingleton("criticalLongInitBean", preInitContext.getBean(CriticalLongInitBean.class));
}

优缺点

  • 优点:完全在Tomcat启动前执行,失败时直接终止,不会启动任何Web相关组件;逻辑清晰,可控性强。
  • 缺点:若核心Bean需要依赖Spring Boot的环境变量(如application.properties配置),需手动将主应用的环境传递到预初始化上下文。

方案2:实现SpringApplicationRunListener(融入Boot生命周期)

通过实现SpringApplicationRunListener接口,在Spring Boot启动的contextPrepared阶段初始化核心Bean。该阶段发生在Web服务器(Tomcat)创建之前,且ApplicationContext已准备好但未刷新。

步骤1:实现SpringApplicationRunListener

public class CriticalBeanPreInitListener implements SpringApplicationRunListener {

    // 必须提供的构造方法
    public CriticalBeanPreInitListener(SpringApplication application, String[] args) {}

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        try {
            // 注册并初始化核心Bean
            context.getBeanFactory().registerSingleton("criticalBean", new CriticalLongInitBean());
            // 若Bean需要依赖注入,可通过扫描方式注册
            // context.scan("com.yourpackage.bean");
            // context.refresh();
            
            // 触发Bean初始化验证
            context.getBean(CriticalLongInitBean.class);
            System.out.println("核心Bean预初始化成功");
        } catch (Exception e) {
            System.err.println("核心Bean预初始化失败,终止应用");
            context.close();
            System.exit(1);
        }
    }

    // 实现其他空方法(满足接口要求)
    @Override public void starting() {}
    @Override public void environmentPrepared(ConfigurableEnvironment environment) {}
    @Override public void contextLoaded(ConfigurableApplicationContext context) {}
    @Override public void started(ConfigurableApplicationContext context) {}
    @Override public void running(ConfigurableApplicationContext context) {}
    @Override public void failed(ConfigurableApplicationContext context, Throwable exception) {}
}

步骤2:注册监听器

在项目的src/main/resources/META-INF/spring.factories文件中添加以下配置,让Spring Boot识别该监听器:

org.springframework.boot.SpringApplicationRunListener=com.yourpackage.listener.CriticalBeanPreInitListener

优缺点

  • 优点:完全融入Spring Boot的启动生命周期,能直接利用主上下文的环境配置;触发时机准确,确保在Tomcat初始化前执行。
  • 缺点:需要实现接口的所有方法,代码略显繁琐;对Spring Boot版本有一定依赖,需确认版本兼容性。

方案3:使用ApplicationContextInitializedEvent事件监听

通过监听ApplicationContextInitializedEvent事件,在ApplicationContext初始化完成但未刷新时触发Bean初始化。该事件的触发时机通常早于Tomcat的创建。

代码示例

@Component
public class CriticalBeanInitListener implements ApplicationListener<ApplicationContextInitializedEvent> {

    @Override
    public void onApplicationEvent(ApplicationContextInitializedEvent event) {
        ConfigurableApplicationContext context = event.getApplicationContext();
        try {
            // 触发核心Bean的实例化与初始化
            context.getBean(CriticalLongInitBean.class);
            System.out.println("核心Bean通过事件监听初始化成功");
        } catch (BeansException e) {
            System.err.println("核心Bean初始化失败,终止应用");
            context.close();
            System.exit(1);
        }
    }
}

注意事项

  • 确保该监听器被Spring Boot的组件扫描机制捕获(如添加@Component注解,或手动注册到SpringApplication)。
  • 部分Spring Boot版本中,该事件的触发时机可能略晚于Tomcat的初始化,需结合实际版本测试验证。

内容的提问来源于stack exchange,提问作者Paul Verest on LinkedIn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 10:35:10