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的启动机制及其实现原理。实际开发中可结合具体需求,合理使用扩展点进行定制化开发。

相关推荐
松仔log1 分钟前
Java中级——组合和继承
android·java·开发语言
不才不才不不才3 分钟前
Spring 源码系列(16): doDispatch 全流程——一次请求的主干链路
java·后端·spring
IT_陈寒10 分钟前
SpringBoot自动配置失效?这个隐藏配置坑了我一整晚
前端·人工智能·后端
搜狐技术产品小编202324 分钟前
告别“黑盒”与误判:如何用“多智能体对抗辩论”重构内容安全审核系统
后端·多agent
snow@li29 分钟前
HikariCP:高性能数据库连接池全景深入梳理
java·数据库
DantyWei1 小时前
controller注册及调用时机
后端
edwarddamon1 小时前
Spring Cloud 配置热更新与 Bean 代理机制梳理
java·后端
码事漫谈1 小时前
我用 Seed Evolving 做了个 AI 小说写作工具
后端
Raas1002 小时前
MAIGateway,魔芋企业级AI网关的FinAPI成本竞争力设计
java·后端·网关·api网关·魔芋ai·finapi·mai gateway
evans在进步2 小时前
Spring AI 从入门到实战:用 Java 实现大模型对话与 Tool Calling
java·人工智能·spring