Spring Boot启动流程及源码实现深度解析

Spring Boot启动流程及源码实现深度解析

一、启动流程概述

Spring Boot的启动流程围绕SpringApplication类展开,核心流程可分为以下几个阶段:

  1. 初始化阶段 :推断应用类型,加载ApplicationContextInitializerApplicationListener
  2. 环境准备:加载配置文件和命令行参数
  3. 上下文创建 :实例化ApplicationContext
  4. 上下文刷新 :执行refresh()方法完成Bean加载
  5. 后置处理 :执行CommandLineRunnerApplicationRunner

二、源码解析

1. 入口类

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

2. SpringApplication初始化

java 复制代码
// SpringApplication.java
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return new SpringApplication(primarySource).run(args);
}

public SpringApplication(Class<?>... primarySources) {
    this(null, primarySources);
}

private SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 推断应用类型
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class)); // 加载Initializers
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 加载Listeners
    this.mainApplicationClass = deduceMainApplicationClass();
}

关键步骤解析

  • deduceFromClasspath()通过类路径判断应用类型(Servlet/Reactive/None)
  • META-INF/spring.factories加载初始化器和监听器

3. run()方法核心流程

java 复制代码
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    
    ConfigurableApplicationContext context = null;
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        
        // 创建应用上下文
        context = createApplicationContext();
        context.setEnvironment(environment);
        
        // 准备上下文
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        
        // 刷新上下文(核心)
        refreshContext(context);
        
        // 后置处理
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        
        // 发布启动完成事件
        listeners.started(context);
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }
    
    listeners.running(context);
    return context;
}

三、关键阶段详解

1. 环境准备(prepareEnvironment)

java 复制代码
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach(environment);
    listeners.environmentPrepared(environment); // 发布环境准备事件
    bindToSpringApplication(environment);
    return environment;
}
  • 加载application.properties/yml文件
  • 处理命令行参数--开头的参数
  • 触发ApplicationEnvironmentPreparedEvent事件

2. 上下文创建(createApplicationContext)

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

java 复制代码
protected ConfigurableApplicationContext createApplicationContext() {
    return this.applicationContextFactory.create(this.webApplicationType);
}

// 默认实现
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
    try {
        switch (webApplicationType) {
            case SERVLET:
                return new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE:
                return new AnnotationConfigReactiveWebServerApplicationContext();
            default:
                return new AnnotationConfigApplicationContext();
        }
    } catch (Exception ex) {
        throw new IllegalStateException(...);
    }
};

3. 上下文刷新(refreshContext)

java 复制代码
private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        } catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

protected void refresh(ApplicationContext applicationContext) {
    ((AbstractApplicationContext) applicationContext).refresh();
}

最终调用AbstractApplicationContext.refresh(),这是Spring容器的核心方法:

java 复制代码
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        // ... [省略其他步骤]
        finishRefresh(); // 触发ContextRefreshedEvent
    }
}

四、关键扩展点

1. ApplicationContextInitializer

java 复制代码
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    void initialize(C applicationContext);
}
  • 执行时机:上下文准备阶段(prepareContext)
  • 配置方式:通过spring.factoriesSpringApplication.addInitializers()

2. ApplicationRunner/CommandLineRunner

java 复制代码
@Component
public class DemoRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) {
        // 应用启动后执行
    }
}
  • 执行顺序:通过@Order注解控制
  • 执行时机:上下文刷新完成后

五、总结

Spring Boot的启动流程通过智能的自动配置和扩展机制,显著简化了Spring应用的初始化过程。理解其核心流程和关键扩展点,可以帮助开发者:

  1. 深入排查启动过程中的问题
  2. 实现自定义的初始化逻辑
  3. 优化应用启动性能
  4. 扩展框架的核心功能

建议结合源码调试工具,通过断点跟踪SpringApplication.run()的执行过程,可以更直观地理解各阶段的实现细节。


流程图文字描述

main()
└─▶ SpringApplication.run()
    ├─▶ 初始化应用类型和扩展组件
    ├─▶ 准备环境(加载配置)
    ├─▶ 创建ApplicationContext
    ├─▶ 准备上下文(Bean定义加载)
    ├─▶ 刷新上下文(Bean初始化)
    ├─▶ 执行Runner接口
    └─▶ 完成启动

通过以上分析,读者可以系统地掌握Spring Boot的启动机制及其实现原理。实际开发中可结合具体需求,合理使用扩展点进行定制化开发。

相关推荐
Asthenia041215 分钟前
chmod 和 chown 区别咋分?记忆小妙招来了!
后端
泉城老铁19 分钟前
使用springboot+Elasticsearch搭建本地化知识库搜索
后端
uhakadotcom21 分钟前
阿里云SchedulerX:分布式任务调度平台入门指南
后端·面试·github
qw94922 分钟前
SpringCloud——Gateway新一代网关
spring boot·spring cloud·gateway
bxp132123 分钟前
springcloud gateway搭建及动态获取nacos注册的服务信息信息
java·spring cloud·gateway
caihuayuan525 分钟前
ApiBoot v2.2.5版本无法兼容Hoxton.SR5的SpringCloud Gateway
java·大数据·spring boot·后端·课程设计
奕川26 分钟前
多模块 Gradle Java 项目发布到新 Maven中央仓库最新指南
后端
用户7851278147027 分钟前
如何使用 Java 获取淘宝分类详情接口(cat_get)
java
舒一笑27 分钟前
云服务器中如何查看服务器具体运行哪些服务
linux·后端
寻月隐君29 分钟前
深入剖析 Go 接口底层实现:从 eface 到 iface(基于 Go 1.24 源码)
后端·go·github