Day01: Spring启动流程:从main()到容器初始化 - 深度解析SpringApplication.run()执行链路

大家好,我是一名Java开发工程师。今天想和大家深入探讨一下Spring Boot应用的启动流程,特别是从我们熟悉的main()方法开始,到Spring容器完全初始化的整个过程。相信很多使用Spring Boot的同学都写过这样的代码:


@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

但你知道这行简单的SpringApplication.run()背后发生了什么吗?让我们一起来揭开它的神秘面纱!

一、Spring Boot启动概览

Spring Boot的启动过程可以大致分为以下几个阶段:

  • SpringApplication实例化阶段
  • 环境准备阶段
  • 应用上下文创建阶段
  • 前置处理阶段
  • 刷新上下文阶段(核心!)
  • 后置处理阶段

今天我们要重点分析的就是第5个阶段——刷新上下文,也就是AbstractApplicationContext.refresh()方法,这是Spring容器初始化的核心。

二、SpringApplication.run()执行链路

让我们先看看SpringApplication.run()的整体调用链路:

// SpringApplication.java
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class<?>[] {primarySource}, args);
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

public ConfigurableApplicationContext run(String... args) {
    // 1. 创建并启动计时器
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();

    // 2. 初始化应用上下文和异常报告器
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

    // 3. 配置headless属性
    configureHeadlessProperty();

    // 4. 获取SpringApplicationRunListeners并启动
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();

    try {
        // 5. 准备环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

        // 6. 打印Banner
        Banner printedBanner = printBanner(environment);

        // 7. 创建应用上下文
        context = createApplicationContext();

        // 8. 准备上下文
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);

        // 9. 刷新上下文(核心!)
        refreshContext(context);

        // 10. 刷新后处理
        afterRefresh(context, applicationArguments);

        // 11. 停止计时器并打印启动日志
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }

        // 12. 发布应用启动完成事件
        listeners.started(context);

        // 13. 执行Runner
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        // 14. 发布应用就绪事件
        listeners.running(context);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

三、核心:AbstractApplicationContext.refresh()源码分析

现在,让我们进入今天的重头戏——AbstractApplicationContext.refresh()方法。这是Spring容器初始化的核心方法,几乎所有重要的初始化工作都在这里完成。

// AbstractApplicationContext.java
@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1. 准备刷新上下文
        prepareRefresh();

        // 2. 获取新的BeanFactory,销毁之前的BeanFactory并创建一个新的
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 3. 准备BeanFactory用于使用此上下文
        prepareBeanFactory(beanFactory);

        try {
            // 4. 允许子类对BeanFactory进行后置处理
            postProcessBeanFactory(beanFactory);

            // 5. 调用BeanFactoryPostProcessor
            invokeBeanFactoryPostProcessors(beanFactory);

            // 6. 注册BeanPostProcessor
            registerBeanPostProcessors(beanFactory);

            // 7. 初始化MessageSource(国际化)
            initMessageSource();

            // 8. 初始化事件广播器
            initApplicationEventMulticaster();

            // 9. 子类扩展点:初始化特殊bean
            onRefresh();

            // 10. 注册监听器
            registerListeners();

            // 11. 初始化所有非懒加载的单例bean
            finishBeanFactoryInitialization(beanFactory);

            // 12. 完成刷新过程,发布相应事件
            finishRefresh();
        } catch (BeansException ex) {
            // 13. 销毁已创建的bean
            destroyBeans();

            // 14. 重置活动标志
            cancelRefresh(ex);

            throw ex;
        }
    }
}

让我们深入分析几个关键步骤:

1. prepareRefresh() - 准备刷新上下文

protected void prepareRefresh() {
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);

    // 初始化占位符资源
    initPropertySources();

    // 验证必要的属性
    getEnvironment().validateRequiredProperties();

    // 早期事件存储
    this.earlyApplicationEvents = new LinkedHashSet<>();
}

2. obtainFreshBeanFactory() - 获取新的BeanFactory

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 子类实现此方法以提供BeanFactory
    refreshBeanFactory();
    return getBeanFactory();
}

GenericApplicationContext中,这个方法的实现很简单:


@Override
protected final void refreshBeanFactory() throws IllegalStateException {
    if (!this.refreshed.compareAndSet(false, true)) {
        throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts");
    }
    this.beanFactory.setSerializationId(getId());
}

5. invokeBeanFactoryPostProcessors() - 调用BeanFactoryPostProcessor

这是Spring Boot自动配置的关键所在:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    // 如果运行在原生镜像中,清除反射缓存
    if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null) {
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}

11. finishBeanFactoryInitialization() - 初始化所有非懒加载的单例bean

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // 初始化ConversionService
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) {
        beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class);
    }

    // 如果没有注册嵌入值解析器,则注册默认的
    if (!beanFactory.hasEmbeddedValueResolver()) {
        beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    }

    // 尽早初始化LoadTimeWeaverAware bean
    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    for (String weaverAwareName : weaverAwareNames) {
        getBean(weaverAwareName);
    }

    // 停止使用临时类加载器进行类型匹配
    beanFactory.setTempClassLoader(null);

    // 允许缓存所有bean定义元数据
    beanFactory.freezeConfiguration();

    // 实例化所有剩余的非懒加载单例bean
    beanFactory.preInstantiateSingletons();
}

四、关键点:Bean的实例化过程

finishBeanFactoryInitialization()方法中调用的beanFactory.preInstantiateSingletons()是实际创建bean实例的地方:

// DefaultListableBeanFactory.java
@Override
public void preInstantiateSingletons() throws BeansException {
    // 获取所有bean定义名称
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

    // 遍历所有bean定义
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            if (isFactoryBean(beanName)) {
                // 处理FactoryBean
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof SmartFactoryBean && ((SmartFactoryBean<?>) bean).isEagerInit()) {
                    getBean(beanName);
                }
            } else {
                // 普通bean的实例化
                getBean(beanName);
            }
        }
    }

    // 所有单例bean初始化完成后,调用SmartInitializingSingleton的回调
    for (String beanName : beanNames) {
        Object singletonInstance = getSingleton(beanName);
        if (singletonInstance instanceof SmartInitializingSingleton) {
            SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
            smartSingleton.afterSingletonsInstantiated();
        }
    }
}

五、实战:自定义Spring启动扩展点

了解Spring启动流程后,我们可以利用这些知识进行扩展。例如,我们可以实现自己的BeanPostProcessor


@Component
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MySpecialBean) {
            System.out.println("Before initialization of " + beanName);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MySpecialBean) {
            System.out.println("After initialization of " + beanName);
        }
        return bean;
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

或者实现ApplicationRunner在应用启动后执行一些逻辑:


@Component
public class MyApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("Application started with arguments: " + Arrays.toString(args.getSourceArgs()));
    }
}

六、总结

Spring Boot的启动流程看似简单,实则背后隐藏着复杂的机制。通过今天的分析,我们可以总结出几个关键点:

  • 环境准备:Spring Boot会先准备好运行环境,包括配置属性、Profile等
  • 上下文创建:根据应用类型(Web/非Web)创建不同的应用上下文
  • Bean工厂准备:创建并配置BeanFactory,这是IoC容器的基础
  • 后置处理器:调用各种BeanFactoryPostProcessor和BeanPostProcessor
  • Bean实例化:初始化所有非懒加载的单例Bean
  • 事件发布:在整个过程中,Spring会发布各种事件通知监听器

理解这些流程对于排查启动问题、进行高级定制都非常有帮助。希望这篇文章能帮助你更深入地理解Spring Boot的启动机制!

如果你对某个具体环节还有疑问,或者想了解更多细节,欢迎在评论区留言讨论。下次我们可能会深入分析Spring Boot的自动配置原理,敬请期待!

思考题:在你的项目中,有没有遇到过Spring启动相关的问题?是如何解决的呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhysunny

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值