Springboot 启动过程及源码分析

Spring Boot 的启动过程是一个高度封装但逻辑清晰的流程,核心围绕 SpringApplication.run() 方法展开,涉及环境准备、上下文初始化、Bean 加载、Web 服务器启动等关键步骤。以下结合源码(基于 Spring Boot 2.7.x)详细拆解启动流程:

核心入口:SpringApplication.run()

所有启动逻辑的起点是主类的 main 方法,调用 SpringApplication.run(主类.class, args),该方法做两件事:

  1. 创建 SpringApplication 实例(初始化启动器)。
  2. 调用实例的 run(String... args) 方法(执行启动流程)。

源码简化如下:

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); // 核心:创建实例并执行run
}

阶段一:SpringApplication 实例初始化(构造方法)

SpringApplication 实例化时会完成基础配置,为后续启动做准备。对应源码:

java

运行

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

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 步骤1:推断应用类型
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 步骤2:加载初始化器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 步骤3:加载监听器
    this.mainApplicationClass = deduceMainApplicationClass(); // 步骤4:推断主类
}
步骤 1:推断应用类型(WebApplicationType.deduceFromClasspath()
  • 功能:根据类路径中是否存在特定类,判断应用类型:
    • SERVLET:存在 ServletTomcat 相关类(默认 Web 应用)。
    • REACTIVE:存在 Reactive 相关类(响应式 Web 应用)。
    • NONE:非 Web 应用。
  • 影响后续创建的 ApplicationContext 类型(如 ServletWebServerApplicationContextReactiveWebServerApplicationContext)。
步骤 2:加载初始化器(ApplicationContextInitializer
  • 功能:从 META-INF/spring.factories 中加载所有 ApplicationContextInitializer 实现类,用于在上下文刷新前自定义配置(如设置环境变量、激活配置文件)。
  • 关键方法:getSpringFactoriesInstances(ApplicationContextInitializer.class),通过 Spring 的 SpringFactoriesLoader 扫描类路径下的 spring.factories 文件。
步骤 3:加载监听器(ApplicationListener
  • 功能:同样从 META-INF/spring.factories 加载所有 ApplicationListener 实现类,用于监听启动过程中的事件(如环境准备完成、上下文刷新等),并执行回调逻辑(如日志打印、健康检查)。
步骤 4:推断主类(deduceMainApplicationClass()
  • 功能:通过栈追踪获取执行 main 方法的类,作为应用主类(后续组件扫描的起点)。

阶段二:执行启动流程(SpringApplication.run(args) 方法)

run 方法是启动的核心,源码简化如下(关键步骤已标注):

java

运行

复制代码
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start(); // 启动计时器(记录启动耗时)
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty(); // 设置无头模式(用于服务器环境,避免图形化依赖)

    // 步骤1:获取启动监听器(SpringApplicationRunListener)
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting(); // 发布启动开始事件(ApplicationStartingEvent)

    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 解析命令行参数

        // 步骤2:准备环境(Environment)
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment); // 配置忽略BeanInfo(优化性能)

        // 步骤3:打印Banner
        Banner printedBanner = printBanner(environment);

        // 步骤4:创建ApplicationContext
        context = createApplicationContext();

        // 步骤5:准备异常报告器(处理启动异常)
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);

        // 步骤6:准备上下文(关联环境、初始化器、Bean定义等)
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);

        // 步骤7:刷新上下文(核心!初始化Bean、启动Web服务器)
        refreshContext(context);

        // 步骤8:刷新后处理(空实现,留给子类扩展)
        afterRefresh(context, applicationArguments);

        stopWatch.stop(); // 停止计时器
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }

        // 步骤9:发布启动完成事件
        listeners.started(context);

        // 步骤10:执行Runner(启动后任务)
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners); // 处理启动异常
        throw new IllegalStateException(ex);
    }

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

    return context; // 返回初始化完成的上下文
}
步骤 1:获取启动监听器(SpringApplicationRunListener
  • 功能:加载 META-INF/spring.factories 中的 SpringApplicationRunListener 实现类(默认 EventPublishingRunListener),用于在启动各阶段发布事件(如 ApplicationStartingEventApplicationEnvironmentPreparedEvent 等),串联整个启动流程。
  • 调用 listeners.starting() 发布 启动开始事件 ,通知所有 ApplicationListener 启动已开始。
步骤 2:准备环境(prepareEnvironment()
  • 功能:初始化并配置 Environment(环境对象,包含系统变量、环境变量、配置文件等),源码如下:

    java

    运行

    复制代码
    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // 1. 创建环境(根据应用类型创建Servlet/Reactive/NONE环境)
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        // 2. 配置环境(加载命令行参数、系统变量、配置文件等)
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        // 3. 绑定环境到SpringApplication(用于后续配置)
        ConfigurationPropertySources.attach(environment);
        // 4. 发布环境准备完成事件(通知监听器修改环境)
        listeners.environmentPrepared(environment);
        // 5. 将环境绑定到应用(方便后续使用)
        bindToSpringApplication(environment);
        // 6. 非Web应用处理
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                    deduceEnvironmentClass());
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }
  • 关键操作:

    • 加载 application.yml/properties 等配置文件(通过 PropertySource 实现)。
    • 激活 spring.profiles.active 指定的配置文件。
    • 发布 ApplicationEnvironmentPreparedEvent 事件,允许监听器动态修改环境(如添加自定义属性)。
  • 功能:根据 spring.main.banner-mode 配置,在控制台打印 Banner(默认是 Spring Boot 标志,可通过 src/main/resources/banner.txt 自定义)。
步骤 4:创建 ApplicationContext(上下文)
  • 功能:根据应用类型创建对应的 ApplicationContext(Spring 核心容器):
    • SERVLETAnnotationConfigServletWebServerApplicationContext
    • REACTIVEAnnotationConfigReactiveWebServerApplicationContext
    • NONEAnnotationConfigApplicationContext
  • 源码逻辑:context = createApplicationContext(),通过反射实例化上下文对象。
步骤 5:准备异常报告器
  • 功能:加载 SpringBootExceptionReporter 实现类,用于在启动失败时生成异常报告(如打印自动配置报告)。
步骤 6:准备上下文(prepareContext()
  • 功能:在上下文刷新前完成初始化,关联环境、加载 Bean 定义等,源码核心逻辑: java

    运行

    复制代码
    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        // 1. 将环境设置到上下文
        context.setEnvironment(environment);
        // 2. 配置上下文(如设置资源加载器、Bean名称生成器)
        postProcessApplicationContext(context);
        // 3. 执行初始化器(ApplicationContextInitializer)
        applyInitializers(context);
        // 4. 发布上下文准备事件(通知监听器)
        listeners.contextPrepared(context);
        // 5. 记录启动日志
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // 6. 注册命令行参数Bean(供其他组件注入)
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        // 7. 禁止BeanFactory缓存BeanDefinition(避免重复加载)
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        // 8. 加载主类Bean定义(将@SpringBootApplication标注的主类注册为Bean)
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));
        // 9. 发布上下文初始化完成事件
        listeners.contextLoaded(context);
    }
  • 关键操作:

    • 执行 ApplicationContextInitializerinitialize() 方法(自定义上下文配置)。
    • 注册 springApplicationArguments 为单例 Bean(可通过 @Autowired 注入命令行参数)。
    • 加载主类及相关组件的 Bean 定义(通过 @ComponentScan 扫描)。
步骤 7:刷新上下文(refreshContext()
  • 功能:这是 Spring 容器的核心初始化步骤,复用 Spring Framework 的 AbstractApplicationContext.refresh() 方法,完成 Bean 实例化、依赖注入、Web 服务器启动等关键操作。

  • 源码核心调用:refresh(context)AbstractApplicationContext.refresh(),其中最关键的步骤包括:

    1. prepareRefresh():验证环境合法性,初始化上下文资源。
    2. obtainFreshBeanFactory() :创建 BeanFactory(默认 DefaultListableBeanFactory),加载所有 Bean 定义。
    3. prepareBeanFactory(beanFactory) :配置 BeanFactory 基础属性(如注册类加载器、Bean 表达式解析器)。
    4. postProcessBeanFactory(beanFactory) :Spring Boot 扩展点,注册 WebServerFactoryCustomizerBeanPostProcessor 等组件,用于处理 Web 服务器配置。
    5. 执行 BeanFactoryPostProcessor :解析 @Configuration 类中的 @Bean 方法,注册 Bean 定义。
    6. 注册 BeanPostProcessor :如 AutowiredAnnotationBeanPostProcessor(处理 @Autowired 依赖注入)。
    7. onRefresh() :Spring Boot 核心扩展,启动嵌入式 Web 服务器(如 Tomcat):
      • 通过 ServletWebServerFactory 创建 Web 服务器实例(如 TomcatServletWebServerFactory.getWebServer())。
      • 绑定端口(默认 8080),启动服务器。
    8. finishBeanFactoryInitialization(beanFactory) :初始化所有非懒加载单例 Bean:
      • 实例化 Bean(调用构造方法)。
      • 填充属性(依赖注入,如 @Autowired)。
      • 执行初始化方法(@PostConstructInitializingBean.afterPropertiesSet())。
    9. finishRefresh() :完成上下文刷新,发布 ContextRefreshedEvent 事件。
步骤 8:刷新后处理(afterRefresh()
  • 功能:空实现,留给子类扩展(如自定义刷新后的逻辑)。
步骤 9:发布启动完成事件(listeners.started(context)
  • 功能:通过 SpringApplicationRunListener 发布 ApplicationStartedEvent 事件,通知监听器 "应用已启动(上下文刷新完成,Web 服务器已启动)"。
步骤 10:执行 Runner(callRunners()
  • 功能:调用所有 ApplicationRunnerCommandLineRunnerrun() 方法(启动后任务),源码:

    java

    运行

    复制代码
    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<>();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners); // 按@Order排序
        for (Object runner : new LinkedHashSet<>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }
  • 作用:在所有 Bean 初始化完成后执行自定义逻辑(如数据加载、缓存预热)。

步骤 11:发布应用就绪事件(listeners.running(context)
  • 功能:发布 ApplicationReadyEvent 事件,通知监听器 "应用已就绪,可接收请求",标志启动流程最终完成。

总结:启动流程核心节点

  1. 初始化 SpringApplication:推断应用类型、加载初始化器和监听器。
  2. 准备环境:加载配置、激活 profiles、发布环境准备事件。
  3. 创建上下文 :根据应用类型实例化 ApplicationContext
  4. 准备上下文:关联环境、注册 Bean 定义、执行初始化器。
  5. 刷新上下文:核心步骤,初始化 Bean、启动 Web 服务器。
  6. 启动后处理:执行 Runner 任务、发布就绪事件。

整个流程通过 事件驱动ApplicationEvent + Listener)和 扩展点InitializerBeanPostProcessor 等)实现高度可定制,这也是 Spring Boot 灵活易用的核心原因。

相关推荐
caron42 小时前
c++ -- 循环依赖解决方案
java·c++·算法
不会kao代码的小王2 小时前
零基础也能搭博客?
linux·windows·后端
七夜zippoe2 小时前
深入理解Java泛型:类型擦除、通配符PECS原则与实践
java·泛型·通配符·类型擦除·pecs
程序员爱钓鱼2 小时前
Python编程实战 - Python实用工具与库 - 爬虫防封与代理机制
后端·python·ipython
程序员爱钓鱼2 小时前
Python编程实战 - Python实用工具与库 - 操作Excel:openpyxl / pandas
后端·python·面试
后端小张2 小时前
【JAVA进阶】SpringBoot启动流程深度解析:从main方法到应用就绪的完整旅程
java·spring boot·后端·spring·spring cloud·java-ee·流程分析
猫头虎2 小时前
Rust评测案例:Rust、Java、Python、Go、C++ 实现五大排序算法的执行时间效率比较(基于 OnlineGDB 平台)
java·开发语言·c++·python·golang·rust·排序算法
爱吃烤鸡翅的酸菜鱼2 小时前
【Java】基于策略模式 + 工厂模式多设计模式下:重构租房系统核心之城市房源列表缓存与高性能筛选
java·redis·后端·缓存·设计模式·重构·策略模式
milanyangbo2 小时前
从局部性原理到一致性模型:深入剖析缓存设计的核心权衡
开发语言·后端·缓存·架构