<#if settings.post_mathjax!false>

关于 Spring Boot 的启动流程,其实核心就做两件事:构建对象和运行启动。
我们可以把它想象成“准备开一家餐厅”的过程。以 经典的2.7.6版本为例,主要流程如下:

  1. 第一阶段:准备工作(new SpringApplication)就像开店前要先确定我们是开火锅店还是奶茶店。
    1. 确定应用类型:Spring Boot 会自动判断当前是Web 应用(Servlet/MVC)、响应式应用(Reactive)还是普通应用(Non-Web Application)。
    2. 加载初始化器和监听器:利用 SPI 机制(读取 spring.factories),把所有需要用到的初始化器和监听器都找出来存好,随时待命。
  2. 第二阶段:正式启动(run 方法) 这是最核心的阶段,就像点火开业,主要分五步走:
    1. 准备环境(Environment):先把配置文件 application.yml环境变量命令行参数都加载好,组成一个环境对象。
    2. 创建容器(ApplicationContext):根据第一步确定的类型,创建一个空的 Spring IOC 容器(比如 AnnotationConfigServletWebServerApplicationContext)。
    3. 刷新容器(Refresh Context),这是最重要的一步。Spring 会解析@SpringBootApplication),扫描所有的 Bean,执行自动配置(AutoConfiguration),把所有组件注入到容器里。关键点:如果是 Web 应用,内嵌的 Tomcat 就是在这个环节(onRefresh 方法)启动的。
    4. 收尾工作:容器刷新完成后,发布启动完成事件,通知所有监听器。
    5. 执行回调(Runners):最后,检查有没有实现了CommandLineRunnerApplicationRunner 的 Bean,如果有,就执行它们的run方法。这通常是我们写开机自启任务的地方。

三种Spring应用对比

类型本质
普通应用一个 Java 程序,main 方法启动,干完活就结束
Web 应用(Servlet)基于 线程阻塞模型 的 HTTP 服务
响应式应用(Reactive)基于 事件驱动 + 非阻塞 的 HTTP 服务

详细分析SpringBoot启动流程

启动入口:main()方法

Spring Boot 应用的启动从包含@SpringBootApplication注解的main()方法开始。@SpringBootApplication是一个组合注解,包含了以下三个核心注解:

  • @SpringBootConfiguration相当于@Configuration表示该类是 Spring 配置类。
  • @EnableAutoConfiguration :启用自动配置功能。
  • @ComponentScan启用组件扫描,默认扫描当前包及其子包下的 Spring 组件(如@Service @Controller @Repository 等)。

@SpringBootConfiguration 本质上是一个组合注解,它内部标注了 @Configuration,因此在功能上与 @Configuration 等价。
不同的是,@SpringBootConfiguration 是 Spring Boot 专用注解,用于标识 应用的主配置类,Spring Boot 在启动过程中会对该注解进行特殊识别,以确定自动配置、生效范围以及组件扫描的基准包,从而增强语义表达并避免配置类的混淆。

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

会触发

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
}

创建 SpringApplication 实例

SpringApplication 是 Spring Boot 启动的核心类,它负责引|导整个 Spring 应用的启动流程。在 run() 方法中,会创建SpringApplication 对象,并根据类路径中的依赖和配置进行初始化。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //推断应用类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList<>(
                    getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    //设置初始化器
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 设置启动监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 确定主应用类
    this.mainApplicationClass = deduceMainApplicationClass();
}

推断应用类型:Spring Boot 会根据类路径中的依赖自动判断应用是 Web 应用、Reactive 应用还是普通的非 Web 应用。常见的应用类型包括:

  • Servlet 应用:如果类路径中存在 javax.servlet.Servlet
  • Reactive 应用:如果类路径中存在org.springframework.web.reactive.DispatcherHandler
  • None 类型:不包含 Web 服务器,仅适用于普通的非 Web 应用。

run()方法详解

创建完 SpringApplication 后,SpringBoot 启动后续所有逻辑都在这个 run 方法里面。这个方法也是 Spring Boot 应用的核心启动流程,它负责启动应用、初始化上下文、执行各种初始化逻辑,并通知相关的监听器和组件。

public ConfigurableApplicationContext run(String... args) {
    // 1.启动时间记录: 通过System.nanoTime()记录应用启动的时间,用于统计整个启动过程的耗时。
    long startTime = System.nanoTime();

	    // 2. 创建 DefaultBootstrapContext,用于在启动过程中保存共享的对象上下文,提供基础的启动支持
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;

    // 3.配置系统属性 "java.awt.headless",适用于没有显示器的服务器环境
    configureHeadlessProperty();

    // 4. 获取 SpringApplicationRunListeners,这些监听器可以监听应用的启动过程, 监听应用的各个生命周期事件。
    SpringApplicationRunListeners listeners = getRunListeners(args);

    // 5. 通知所有监听器,Spring Boot 应用程序即将启动
    listeners.starting(bootstrapContext, this.mainApplicationClass);

    try {
        // 6. 解析传入的命令行参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

        // 7. 准备环境,加载配置文件、环境变量、命令行参数等
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

        // 8. 配置环境,决定是否忽略 Java 的 `BeanInfo` 类,以加快启动速度
        configureIgnoreBeanInfo(environment);

        // 9. 打印 Banner(欢迎信息),通常会在控制台或日志中展示
        Banner printedBanner = printBanner(environment);

        // 10. 创建应用上下文(ApplicationContext),可以是不同类型的 Web 或非 Web 上下文
        context = createApplicationContext();

        // 11.设置应用启动监控器,用于收集应用启动过程中的统计数据
        context.setApplicationStartup(this.applicationStartup);

        // 12.准备应用上下文,注入环境、事件监听器、命令行参数等
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

        // 13.刷新上下文,启动 Spring 的核心容器,完成 Bean 的初始化等过程,其间会触发内嵌的 web 容器
        refreshContext(context);

        // 14.上下文刷新后的回调操作,可以用于自定义初始化逻辑
        afterRefresh(context, applicationArguments);

        // 15.计算应用启动耗时
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);

        // 16.如果配置了日志,输出启动耗时等信息
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
        }

        // 17.通知监听器,应用上下文已启动完成
        listeners.started(context, timeTakenToStartup);

        // 18.执行 CommandLineRunner 或 ApplicationRunner 接口中的代码
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        // 19.处理启动过程中发生的异常
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        // 20.计算到应用完全准备好所需的时间
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);

        // 21.通知监听器,应用已经完全准备好
        listeners.ready(context, timeTakenToReady);
    }
    catch (Throwable ex) {
        // 22.处理应用准备过程中的异常
        handleRunFailure(context, ex, null);
        throw new IllegalStateException(ex);
    }

    // 23.返回创建好的应用上下文
    return context;
}

启动监听器

启动监听器 ApplicationListener在 Spring Boot 启动过程中起到监听特定事件的作用。Spring Boot 中的事件机制会触发不同的生命周事件,比如:

  • ApplicationStartingEvent :在应用启动时触发。
  • ApplicationEnvironmentPreparedEvent :在环境变量和配置文件准备完成时触发。
  • ApplicationPreparedEvent :在 Spring 容器(ApplicationContext )准备完成时触发。
    这些事件都可以通过 ApplicationListener 进行监听,使得可以在应用启动过程中进行自定义处理

准备环境 ConfigurableEnvironment

在启动过程中,Spring Boot 通过 configurableEnvironment 准备应用的环境变量。它会读取配置文件(如 application.propertiesapplication.yml ),并加载系统环境变量、命令行参数等。Spring Boot 使用PropertySource 来管理这些配置属性。
激活配置文件:Spring Boot 支持通过spring.profiles.active配置信息 激活不同的配置文件,以实现开发、测试、生产环境的灵活切换。

创建 ApplicationContext

创建 ApplicationContext,会注册所有配置类和自动配置类Spring Boot 使用@EnableAutoConfiguration自动加载符合条件的配置类。自动配置机制基于条件注解(如@Conditional0nClass @ConditionalOnMissingBean等),在满足条件时自动注入相应的 Bean (自动装配)

  • 加载配置类:包括开发者定义的@Configuration 类以及 Spring Boot 提供的自动配置类(如DataSourceAutoConfiguration WebMvcAutoConfiguration 等)。
  • 注册 BeanPostProcessor :Spring 的 Bean 生命周期会经过BeanPostProcessor的处, 这些包括依赖注入,AOP代理等操作。

刷新 ApplicationContext

它负责完成以下工作:

  • 创建并初始化所有单例 Bean: Spring 容器会创建并初始化所有定义的单例 Bean。这里包括开发者定义的 Bean 以及自动配置注入的 Bean。
  • 处理 BeanPostProcessor:在 Bean 初始化之前和之后,Spring 通过 BeanPostProcessor 对 Bean 进行加工,例如为某些 Bean 创建 AOP 代理对象。
  • 事件发布与监听:在容器刷新过程中,Spring 会发布ContextRefreshedEvent,通知监听器应用上下文刷新完成。

启动嵌入式 Web服务器 (如 Tomcat)

对于 Web 应用,Spring Boot 会自动启动嵌入式 Web 服务器(默认是Tomcat)。这一步是在刷新refresh()方法的最后阶段进行的,主要包括以下操作:

  • 注册内置的 Servlet 和 Filter: Spring Boot 会自动注册 DispatcherServlet ErrorPageFilter 等 Servlet 和 Filter。
  • 启动嵌入式 Web 容器:Spring Boot 根据应用的类型自动选择 Web 容器,并绑定到指定的端口上监听 HTTP 请求。

执行 CommandLineRunnerApplicationRunner

Spring Boot 在应用启动完成后,会调用实现了CommandLineRunner``或ApplicationRunner 接口的 Bean。这两个接口适合在应用启动后执行一些初始化逻辑。

@Component
public class MyStartupRunner implements CommandLineRunner {
  @Override
  public void run(String... args) throws Exception {
      System.out.println("^_^");
  }
}

本站由 Rizxfrog 使用 Stellar 创建。