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 灵活易用的核心原因。

相关推荐
北冥you鱼4 分钟前
Go flag 包详解:从命令行解析到实战应用
开发语言·后端·golang
月落星还在16 分钟前
Spring MVC 与 Spring Boot:从“手动挡”到“自动驾驶”的进化论,兼谈前后端分离的哲学
spring boot·spring·云原生·mvc
蓝田~30 分钟前
大模型本地部署与远程调用 — 从 API 到 Agent
java·人工智能·claude·claude code
码农进化录36 分钟前
Java 程序员的 AI 进化论 | Spring Boot 接入 OpenAI 的六个坑,全帮你踩了
java·spring boot·openai
她说彩礼65万39 分钟前
Asp.net core 模型绑定
后端·asp.net
用户342323237631744 分钟前
BLE 传感器节点实战
后端
疯狂成瘾者1 小时前
Java 常见集合方法
java·windows·python
从此以后自律1 小时前
ConcurrentHashMap 超详细详解
java
fliter1 小时前
Go 1.26 新增 `bytes.Buffer.Peek`:只看数据,不移动读取位置
后端
羑悻1 小时前
我受够了 Coding Agent 的“假聪明,真串行”,于是做了 Meta-Orchestrator!
后端