25.Spring Boot 启动流程深度解析:从run()到自动配置

Spring Boot 启动流程深度解析:从run()到自动配置

  1. 前言 - 引入主题
  2. 启动流程概览 - 宏观视角
  3. ApplicationContext初始化 - 核心组件
  4. prepareContext详解 - 准备阶段
  5. refreshContext流程 - 刷新机制
  6. 自动配置原理 - 深度剖析
  7. 实践验证 - 代码示例
  8. 时序图 - 可视化流程
  9. 源码解析 - 核心代码
  10. 常见问题 - 实战经验
  11. 总结 - 知识提炼

前言

Spring Boot作为现代Java开发的事实标准,其"约定优于配置"的设计理念大大简化了应用开发。但当我们运行SpringApplication.run()时,底层究竟发生了什么?本文将深入剖析Spring Boot的启动流程,揭示自动配置的奥秘。

一、启动流程概览

1.1 核心入口

java 复制代码
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

看似简单的一行代码,背后却经历了复杂而精密的初始化过程。

1.2 启动流程关键步骤

Spring Boot的启动主要分为以下几个阶段:

  1. 创建SpringApplication实例
  2. 准备Environment(环境)
  3. 创建ApplicationContext(应用上下文)
  4. 准备Context(prepareContext)
  5. 刷新Context(refreshContext)
  6. 完成启动

二、深入核心:ApplicationContext的初始化

2.1 ApplicationContextInitializer接口

在Spring Boot启动过程中,ApplicationContextInitializer扮演着重要角色:

java 复制代码
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    void initialize(C applicationContext);
}

作用: 在ApplicationContext刷新之前进行自定义初始化,比如添加BeanFactoryPostProcessor、注册监听器等。

2.2 创建ApplicationContext

第二步是创建ApplicationContext实例,根据应用类型(Servlet、Reactive、None)创建对应的上下文:

java 复制代码
protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        switch (this.webApplicationType) {
            case SERVLET:
                contextClass = AnnotationConfigServletWebServerApplicationContext.class;
                break;
            case REACTIVE:
                contextClass = AnnotationConfigReactiveWebServerApplicationContext.class;
                break;
            default:
                contextClass = AnnotationConfigApplicationContext.class;
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

三、prepareContext()方法详解

prepareContext()方法是启动流程中的关键环节,它完成了以下核心任务:

3.1 设置环境

java 复制代码
context.setEnvironment(environment);

将准备好的Environment设置到ApplicationContext中。

3.2 应用初始化器

java 复制代码
applyInitializers(context);

调用所有ApplicationContextInitializerinitialize()方法。

3.3 加载Bean定义

通过load()方法加载主配置类及其他配置:

java 复制代码
protected void load(ApplicationContext context, Object[] sources) {
    BeanDefinitionLoader loader = createBeanDefinitionLoader(
        getBeanDefinitionRegistry(context), sources);
    loader.load();
}

这一步会:

  • 注解扫描(@ComponentScan)
  • 导入配置类(@Import)
  • 加载XML配置
  • 注册主配置类为BeanDefinition

四、refreshContext():核心刷新流程

refreshContext()调用AbstractApplicationContext的refresh()方法,这是Spring容器启动的核心:

4.1 BeanDefinition注册

invokeBeanFactoryPostProcessors()阶段:

java 复制代码
@Override
public void refresh() throws BeansException {
    // ...
    // 调用BeanFactory后处理器
    invokeBeanFactoryPostProcessors(beanFactory);
    
    // 注册Bean后处理器
    registerBeanPostProcessors(beanFactory);
    
    // 初始化消息源
    initMessageSource();
    
    // 初始化事件多播器
    initApplicationEventMulticaster();
    
    // 刷新特定上下文子类
    onRefresh();
    
    // 注册监听器
    registerListeners();
    
    // 实例化所有非懒加载的单例Bean
    finishBeanFactoryInitialization(beanFactory);
    
    // 完成刷新
    finishRefresh();
}

4.2 自动配置的加载

在这个阶段,Spring Boot会:

  1. 读取spring.factories :加载META-INF/spring.factories中配置的自动配置类
  2. 条件评估 :根据@Conditional注解判断是否需要加载
  3. Bean实例化:创建并注册符合条件的Bean
  4. 属性绑定:将配置文件中的属性绑定到Bean

五、自动配置原理深度剖析

5.1 @SpringBootApplication注解

java 复制代码
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
}

5.2 @EnableAutoConfiguration

java 复制代码
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

AutoConfigurationImportSelector会:

  1. spring.factories加载自动配置类
  2. 通过@Conditional系列注解进行条件判断
  3. 排除不需要的配置
  4. 返回需要导入的配置类全限定名

5.3 条件注解示例

java 复制代码
@Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
public class DataSourceAutoConfiguration {
    // 自动配置DataSource
}

六、实践验证:自定义Initializer

6.1 创建自定义Initializer

java 复制代码
public class MyApplicationContextInitializer 
    implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    
    @Override
    public void initialize(ConfigurableApplicationContext context) {
        System.out.println("=== 自定义初始化器执行 ===");
        
        // 注册自定义BeanDefinition
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getBeanFactory();
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(MyBean.class);
        registry.registerBeanDefinition("myBean", beanDefinition);
    }
}

6.2 注册方式

方式一:spring.factories

META-INF/spring.factories中配置:

properties 复制代码
org.springframework.context.ApplicationContextInitializer=\
com.example.MyApplicationContextInitializer

方式二:编程方式

java 复制代码
SpringApplication app = new SpringApplication(Application.class);
app.addInitializers(new MyApplicationContextInitializer());
app.run(args);

七、启动流程时序图

复制代码
用户代码
   ↓
SpringApplication.run()
   ↓
创建SpringApplication实例
   ↓
准备Environment
   ↓
创建ApplicationContext
   ↓
prepareContext()
   ├── 设置Environment
   ├── 应用Initializers
   ├── 发布事件
   └── 加载Bean定义
   ↓
refreshContext()
   ├── prepareBeanFactory()
   ├── invokeBeanFactoryPostProcessors()
   │    └── 处理自动配置类
   ├── registerBeanPostProcessors()
   ├── finishBeanFactoryInitialization()
   │    └── 实例化Bean
   └── finishRefresh()
        └── 发布启动完成事件
   ↓
启动完成

八、核心源码解析

8.1 SpringApplication.run()核心代码

java 复制代码
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    
    // 1. 准备环境
    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    
    // 2. 打印Banner
    Banner printedBanner = printBanner(environment);
    
    // 3. 创建ApplicationContext
    context = createApplicationContext();
    
    // 4. 准备上下文
    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    
    // 5. 刷新上下文(核心)
    refreshContext(context);
    
    // 6. 刷新后处理
    afterRefresh(context, applicationArguments);
    
    stopWatch.stop();
    
    return context;
}

8.2 自动配置加载源码

java 复制代码
public class AutoConfigurationImportSelector {
    
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, 
                                                      AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
            getSpringFactoriesLoaderFactoryClass(), 
            getBeanClassLoader());
        
        return configurations;
    }
    
    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }
}

九、常见问题与最佳实践

9.1 Bean定义冲突

问题: 多个自动配置类尝试注册同名Bean

解决: 使用@ConditionalOnMissingBean确保只在Bean不存在时才注册

9.2 循环依赖

问题: Bean之间存在循环依赖导致启动失败

解决:

  • 重构代码,消除循环依赖
  • 使用@Lazy延迟加载
  • 使用Setter注入代替构造器注入

9.3 自动配置不生效

排查步骤:

  1. 检查spring.factories配置是否正确
  2. 验证@Conditional条件是否满足
  3. 使用--debug启动查看自动配置报告
  4. 检查是否被exclude排除

9.4 启动速度优化

优化建议:

  1. 排除不需要的自动配置
  2. 使用Lazy Initialization(Spring Boot 2.2+)
  3. 合理使用@ComponentScan范围
  4. 考虑使用GraalVM Native Image

十、总结

Spring Boot的启动流程体现了精妙的设计:

  1. 分层清晰:从Environment准备到Context创建,再到Bean初始化,层次分明
  2. 扩展性强:通过Initializer、Listener、BeanPostProcessor等提供多个扩展点
  3. 自动化:自动配置机制大幅降低了配置复杂度
  4. 条件化:@Conditional系列注解实现灵活的配置加载

理解启动流程不仅有助于排查问题,更能帮助我们更好地利用Spring Boot的扩展机制,构建更加优雅和高效的应用。

参考资源


💡 提示: 建议结合源码调试,逐步跟踪启动流程,加深理解。

相关推荐
Rover.x4 小时前
Spring国际化语言切换不生效
java·后端·spring
Sunny_yiyi4 小时前
Java接入飞书发送通知消息
java·windows·飞书
Momentary_SixthSense4 小时前
serde
开发语言·rust·json
MediaTea4 小时前
Python 文件操作:JSON 格式
开发语言·windows·python·json
Paxon Zhang4 小时前
数据结构之**二叉树**超全秘籍宝典2
java·数据结构·算法
2501_930707784 小时前
使用C#代码添加或删除PPT页面
开发语言·c#·powerpoint
百锦再4 小时前
金仓数据库提出“三低一平”的迁移理念
开发语言·数据库·后端·python·rust·eclipse·pygame
Mos_x4 小时前
【Spring Boot】Spring Boot解决循环依赖
java·spring boot·spring
茉莉玫瑰花茶4 小时前
从零搭建 C++ 在线五子棋对战项目:从环境到上线,全流程保姆级教程
开发语言·c++