Spring Boot启动流程深度解析(源码级剖析)

关键词:Spring Boot、启动流程、源码解析、IoC容器、自动配置

摘要:本文深入分析Spring Boot的启动流程,结合核心源码详解从run()方法到嵌入式服务器启动的每一步操作,揭秘自动配置原理及扩展点。

一、为什么需要掌握启动流程?

Spring Boot以其约定大于配置快速启动的特性成为Java微服务开发的首选框架。理解其启动流程,能帮助我们:

  • 🔧 定制化扩展:自定义启动加载过程
  • 性能优化:加速应用启动(大型项目启动耗时从分钟级降到秒级)
  • 🐞 故障排查:解决初始化阶段的诡异Bug
  • 💡 深入理解框架:打通Spring Boot任督二脉

下面我们将结合源码,深入剖析Spring Boot启动的每一个关键步骤。

二、启动流程总览(10步图解)

flowchart TB A[SpringApplication.run()] --> B[初始化SpringApplicationRunListeners] B --> C[准备环境Environment] C --> D[打印Banner] D --> E[创建ApplicationContext] E --> F[准备上下文] F --> G[刷新上下文
(最核心)] G --> H[加载自动配置] G --> I[实例化单例Bean] I --> J[启动嵌入式服务器] J --> K[发布Ready事件]

三、启动流程源码级分解

步骤1: SpringApplication初始化

java 复制代码
// 源码位置:SpringApplication.java
public SpringApplication(Class<?>... primarySources) {
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 1.推断应用类型
    this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class)); 
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 2.加载Initializer
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 3.加载Listener
    this.mainApplicationClass = deduceMainApplicationClass(); // 4.推断主类
}

作用说明:

  1. 推断应用类型(Servlet/Reactive/None)通过检测类路径是否存在:

    • javax.servlet.Servletorg.springframework.web.context.ConfigurableWebApplicationContextSERVLET
    • org.springframework.web.reactive.DispatcherHandlerREACTIVE
    • 否则 → NONE
  2. 通过SPI机制加载META-INF/spring.factories中定义的拓展组件

步骤2: 执行run()方法

java 复制代码
// 源码位置:SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
    // 1. 启动计时器
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    
    // 2. 初始化SpringApplicationRunListeners
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting(); // 发布ApplicationStartingEvent
    
    // 3. 准备环境
    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    
    // 4. 打印Banner
    Banner printedBanner = printBanner(environment);
    
    // 5. 创建ApplicationContext
    context = createApplicationContext();
    
    // 6. 准备上下文
    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    
    // 7. 刷新上下文(核心!)
    refreshContext(context);
    
    // 8. 执行Runner
    callRunners(context, applicationArguments);
    
    // 9. 完成启动
    listeners.started(context);
    return context;
}

步骤3: 环境准备(Environment)

java 复制代码
private ConfigurableEnvironment prepareEnvironment(...) {
    // 创建环境对象(Servlet环境或标准环境)
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 加载配置源(application.properties/yml)
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    // 发布ApplicationEnvironmentPreparedEvent
    listeners.environmentPrepared(environment);
    // 绑定SpringApplication配置
    bindToSpringApplication(environment);
    return environment;
}

步骤4: 创建IoC容器

根据应用类型创建不同类型的上下文:

java 复制代码
// 源码位置:SpringApplication.java
protected ConfigurableApplicationContext createApplicationContext() {
    return this.applicationContextFactory.create(this.webApplicationType);
}

// 默认实现
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
    switch (webApplicationType) {
        case SERVLET:
            return new AnnotationConfigServletWebServerApplicationContext();
        case REACTIVE:
            return new AnnotationConfigReactiveWebServerApplicationContext();
        default:
            return new AnnotationConfigApplicationContext();
    }
};

步骤5: 刷新上下文(最核心步骤)

java 复制代码
// 源码位置:SpringApplication.java
private void refreshContext(ConfigurableApplicationContext context) {
    // 调用AbstractApplicationContext.refresh()
    refresh(context);
}

// 源码位置:AbstractApplicationContext.java
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // [1] 准备刷新工作
        prepareRefresh();
        
        // [2] 获取新的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();
        }
    }
}

关键子步骤:自动配置生效过程

java 复制代码
// 源码位置:PostProcessorRegistrationDelegate.java
public static void invokeBeanFactoryPostProcessors(...) {
    // 处理BeanDefinitionRegistryPostProcessor
    processConfigBeanDefinitions(registry);
}

private static void processConfigBeanDefinitions(...) {
    // 创建ConfigurationClassParser解析@Configuration类
    parser.parse(candidates);
    
    // 加载自动配置类
    Set<String> candidates = new LinkedHashSet<>(configCandidates);
    AutoConfigurationImportSelector.selectImports() // 关键方法
}

自动配置选择器核心逻辑:

java 复制代码
// 源码位置:AutoConfigurationImportSelector.java
protected List<String> getCandidateConfigurations(...) {
    // 从META-INF/spring.factories加载配置
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
        EnableAutoConfiguration.class, getBeanClassLoader());
    
    // 应用条件过滤(@ConditionalOnClass等)
    configurations = filter(configurations, autoConfigurationMetadata);
    return configurations;
}

四、关键扩展点实战

扩展点1:自定义ApplicationRunner

java 复制代码
@Component
@Order(1) // 控制执行顺序
public class CacheInitializer implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        System.out.println("初始化缓存数据...");
        // 实际业务逻辑
    }
}

扩展点2:自定义事件监听

java 复制代码
@Component
public class StartupMonitorListener implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        System.out.println("应用启动完成,耗时:" + 
            TimeUnit.NANOSECONDS.toMillis(event.getTimestamp()) + "ms");
    }
}

扩展点3:自定义BeanPostProcessor

java 复制代码
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if(bean instanceof Controller) {
            System.out.println("Controller初始化完成: " + beanName);
        }
        return bean;
    }
}

五、性能优化实践

  1. 减少组件扫描范围
java 复制代码
@SpringBootApplication(scanBasePackages = "com.example.core")
  1. 排除不必要的自动配置
java 复制代码
@EnableAutoConfiguration(exclude = {
    DataSourceAutoConfiguration.class, 
    KafkaAutoConfiguration.class
})
  1. 启用懒初始化(慎用)
properties 复制代码
spring.main.lazy-initialization=true
  1. 关闭JMX监控(节省30-50ms)
properties 复制代码
spring.jmx.enabled=false

六、常见面试题

Q1: @SpringBootApplication注解做了哪些事?

A: 该注解是三个核心注解的组合:

  • @SpringBootConfiguration:标识为配置类
  • @EnableAutoConfiguration:启用自动配置
  • @ComponentScan:组件扫描(默认当前包)

Q2: 自动配置是如何实现的?

A: 通过@EnableAutoConfiguration引入AutoConfigurationImportSelector,该选择器:

  1. META-INF/spring.factories加载配置类
  2. 过滤掉缺少依赖的配置(通过@ConditionalOnClass等条件注解)
  3. 应用排序规则

Q3: 如何加速Spring Boot启动?

优化方案:

  1. 减小@ComponentScan范围
  2. 排除不必要的自动配置
  3. 使用@Lazy延迟初始化
  4. 关闭JMX、actuator等非必要模块

七、总结

Spring Boot的启动过程本质上是IoC容器的初始化过程,核心亮点在于:

  1. 事件驱动架构:通过11种启动事件解耦各阶段
  2. 条件化自动配置:按需加载超过300+自动配置类
  3. 嵌入式容器:无缝集成Tomcat/Jetty等服务器
  4. SPI扩展机制:通过spring.factories实现插件化

掌握启动流程后,你可以:

  • 定制自己的启动初始化逻辑
  • 解决Bean初始化顺序问题
  • 实现秒级启动的微服务
  • 深度优化Spring Boot应用
相关推荐
weixin_464307632 小时前
QT智能指针
java·数据库·qt
架构师沉默2 小时前
程序员如何避免猝死?
java·后端·架构
Zzxy2 小时前
快速搭建SpringBoot项目并整合MyBatis-Plus
java·spring boot
星如雨グッ!(๑•̀ㅂ•́)و✧2 小时前
WebFlux onErrorContinue 和 onErrorResume使用详解
java·人工智能
电商API&Tina2 小时前
电商数据采集API接口||合规优先、稳定高效、数据精准
java·javascript·数据库·python·json
zjjsctcdl3 小时前
springBoot发布https服务及调用
spring boot·后端·https
观测云3 小时前
SpringBootAI 接入观测云 MCP 最佳实践
spring boot·观测云·mcp
zdl6863 小时前
Spring Boot文件上传
java·spring boot·后端
世界哪有真情3 小时前
哇!绝了!原来这么简单!我的 Java 项目代码终于被 “拯救” 了!
java·后端
RMB Player3 小时前
Spring Boot 集成飞书推送超详细教程:文本消息、签名校验、封装工具类一篇搞定
java·网络·spring boot·后端·spring·飞书