tulip notes
首页
  • 学习笔记

    • 《Vue》
  • 踩坑日记

    • JavaScript
  • MQ
  • Nginx
  • IdentityServer
  • Redis
  • Linux
  • Java
  • SpringBoot
  • SpringCloud
  • MySql
  • docker
  • 算法与设计模式
  • 踩坑与提升
  • Git
  • GitHub技巧
  • Mac
  • 网络
  • 项目构建合集
  • 一些技巧
  • 面试
  • 一些杂货
  • 友情链接
  • 项目发布
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Star-Lord

希望一天成为大师的学徒
首页
  • 学习笔记

    • 《Vue》
  • 踩坑日记

    • JavaScript
  • MQ
  • Nginx
  • IdentityServer
  • Redis
  • Linux
  • Java
  • SpringBoot
  • SpringCloud
  • MySql
  • docker
  • 算法与设计模式
  • 踩坑与提升
  • Git
  • GitHub技巧
  • Mac
  • 网络
  • 项目构建合集
  • 一些技巧
  • 面试
  • 一些杂货
  • 友情链接
  • 项目发布
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 探索SpringBoot

    • 初步认识SpringBoot
    • 自定义starter
    • 一些常用注解
    • 自动装配
    • spring中开启事务
    • Spring中事务的属性
    • SpringBoot启动流程
      • 为什么了解它
        • 大致过程
      • 启动类
        • 创建SpringApplication
        • 主要完成的任务
        • 运行SpringApplication
        • run方法
        • 流程分析
        • 概要
      • 总结
    • Spring中Bean的创建跟循环依赖
    • IOC的实现机制是什么
  • 常用功能实现

  • Security认证授权

  • 扩展

  • 实战与注意事项

  • 其它

  • 《SpringBoot》笔记
  • 探索SpringBoot
EffectTang
2023-10-09
目录

SpringBoot启动流程

# SpringBoot启动流程

下列源码采用的SpringBoot 2.6.3

# 为什么了解它

理解 Spring Boot 的启动流程看似复杂,但实际上它是 Spring Boot 简化开发的核心机制。学习它的意义不在于死记硬背步骤,而在于掌握其设计思想,从而在开发中更高效地解决问题(如配置失效、Bean初始化异常、性能优化等)。

# 大致过程

Spring Boot 的启动流程可以简化为 4 个关键阶段:

  1. 环境准备(Environment Preparation)

    • 加载 application.yml、application.properties、环境变量等配置。
    • 初始化 SpringApplication 对象,解析启动参数(如 --server.port=8080)。
    • 关键类:ConfigurableEnvironment, PropertySource。
  2. 上下文创建(Application Context Creation)

    • 创建 ApplicationContext(默认是 AnnotationConfigServletWebServerApplicationContext)。
    • 加载 @SpringBootApplication 注解标识的主配置类。
    • 关键类:BeanFactory, BeanDefinition。
  3. Bean 加载与自动配置(Bean Loading & Auto-Configuration)

    • 扫描 @ComponentScan 指定的包路径,注册 Bean。
    • 执行 @EnableAutoConfiguration,通过 spring.factories 加载自动配置类(如 DataSourceAutoConfiguration)。
    • 关键类:ConfigurationClassPostProcessor, AutoConfigurationImportSelector。
  4. Web 服务器启动(Web Server Startup)

    • 根据依赖自动选择内嵌服务器(Tomcat/Jetty/Netty)。

    • 启动 Servlet 容器,初始化 DispatcherServlet。

    • 关键类:ServletWebServerFactory, DispatcherServletAutoConfiguration。

# 启动类

每个SpringBoot项目都有一个启动类,该类用@SpringBootApplication标注着,程序的启动都从这里开始。

@SpringBootApplication
public class ClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ClientApplication.class, args);
    }
}
1
2
3
4
5
6
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }
// 1-先构建SpringApplication  
// 2-再执行run方法
    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }
1
2
3
4
5
6
7
8

进到run方法中,可以看到它分为2个部分:

  • 执行构造方法-构建SpringApplication
  • run方法的执行

# 创建SpringApplication

在构造SpringApplication时,部分源代码如下

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

先传入一个ResourceLoader

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = Collections.emptySet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
        this.applicationStartup = ApplicationStartup.DEFAULT;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
  // 获取应用类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
  //初始化引导器 bootstrapRegistryInitializers
        this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
      //设置初始化器
  this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //设置监听器
 this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));//定位主类
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 主要完成的任务

在构建过程中,大致完成了如下几件事:

  • 设置一些初始值
  • 判断是否加载Servlet,来判断是否是Web环境
  • 初始化启动引导器bootstrapRegistryInitializers
  • 设置初始化器Initializers
  • 设置监听器Listeners
  • 定位主类(根据main方法所在,找到主类)

初始化引导器、初始化器、监听器都是从META-INF/spring.factories文件中定义的名称去查找

# 运行SpringApplication

# run方法

下列为执行run方法的源码:

public ConfigurableApplicationContext run(String... args) {
        long startTime = System.nanoTime();
  			// 创建-引导上下文 bootstrapContext
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
  			// 创建配置环境上下文
        ConfigurableApplicationContext context = null;
  			// 进入 headless 模式
        this.configureHeadlessProperty();
  			// 获取所有运行时 监听器 
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);

        try {
          	// 保存命令行传过来的程序参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
          // 准备环境 environment
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
          // 配置 忽略一些bean
            this.configureIgnoreBeanInfo(environment);
          // 打印banner
            Banner printedBanner = this.printBanner(environment);
          	// 创建`IOC`容器
            context = this.createApplicationContext();
          	// 设置一个启动器,设置应用程序启动
            context.setApplicationStartup(this.applicationStartup);
          // 准备IOC容器的基本信息
            this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
          // 刷新IOC容器   
          this.refreshContext(context);
          // 执行刷新后的处理
            this.afterRefresh(context, applicationArguments);
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
            }
						// 通知所有的监听器
            listeners.started(context, timeTakenToStartup);
          // 所有Runner 执行run方法,设置初始化数据
            this.callRunners(context, applicationArguments);
        } catch (Throwable var12) {
            this.handleRunFailure(context, var12, listeners);
            throw new IllegalStateException(var12);
        }

        try {
            Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
            listeners.ready(context, timeTakenToReady);
            return context;
        } catch (Throwable var11) {
            this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var11);
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

# 流程分析

  • 首先记录一个程序开始时间
  • 创建引导上下文(bootstrapContext)
    • 之前在构造方法时,创建的BootstrapRegistryInitializer就会被加载,用于完成对引导启动上下文的环境设置
  • 创建配置环境上下文(context)
  • 让当前应用进入headless模式。
  • 获取所有 RunListener(运行监听器)【为了方便所有Listener进行事件感知】
    • 遍历 SpringApplicationRunListener 调用 starting 方法,开始监听
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
        this.doWithListeners("spring.boot.application.starting", (listener) -> {
            listener.starting(bootstrapContext);
        }, (step) -> {
            if (mainApplicationClass != null) {
                step.tag("mainApplicationClass", mainApplicationClass.getName());
            }

        });
    }
1
2
3
4
5
6
7
8
9
10
  • 保存命令行传过来的程序参数(它优先于项目里面的配置),如: java -jar --spring.profiles.active=prod
  • 准备环境 prepareEnvironment
    • 创建环境
    • 读取所有的配置源的配置属性值
    • 绑定环境信息
    • 监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成
    • 绑定当前环境Environment 到SpringApplication上
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach((Environment)environment);
        listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
        DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment);
        Assert.state(!((ConfigurableEnvironment)environment).containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        if (!this.isCustomEnvironment) {
            environment = this.convertEnvironment((ConfigurableEnvironment)environment);
        }

        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 配置忽略的 bean

  • 打印banner

  • 根据项目类型(Servlet)创建IOC容器

  • 设置一个启动器,应用程序启动

  • 配置IOC容器的基本信息 prepareContext()

    • 保存环境信息

    • IOC容器的后置处理流程

    • 应用初始化器;applyInitializers,来对容器进行初始化扩展 (把在构建SpringApplication时,创建的Initializers,全部初始化)

    • 最后所有监听器 加载配置环境上下文 ——listeners.contextLoaded(context);

  • 刷新IOC容器,refreshContext()

    • 创建容器中所有组件(涉及容器启动跟自动装配)
  • 执行刷新后的操作 afterRefresh()

    • 本身没有内容,是留给用户自定义容器刷新完成后的处理逻辑
  • 所有监听器 调用 listeners.started(context); 通知所有的监听器 ioc已经ok

  • 调用所有runners;callRunners()

    • 获取容器中的 ApplicationRunner
    • 获取容器中的 CommandLineRunner
    • 合并所有runner并且按照@Order进行排序
    • 遍历所有的runner。调用 run 方法

    Spring中有两种Runner,ApplicationRunner跟CommandLineRunner.它们都是接口

    它的作用是进行一些初始化的操作,比如预先加载并缓存某些数据,读取某些配置等等。

    这两个接口可以在 Spring 的环境下指定一个 Bean 运行(run)某些你想要做的事情,如果你有多个 Bean 进行指定,那么可以通过 Ordered 接口或者 @Order 注解指定执行顺序。

  • 所有监听器 listeners.ready(context, timeTakenToReady);告知一切准备就绪,

# 概要

  • 准备一些环境跟配置信息
  • 创建启动引导上下文、环境配置上下文
  • 启动所有监听器-进行监听
  • 准备ioc容器(准备ioc环境,创建ioc容器,最后刷新它)
  • 程序中的Runner执行run方法

其中核心的是ioc的加载。

# 总结

  1. 启动SpringBoot应用程序,会先创建SpringApplication对象,在构造方法中进行某些参数的设置、初始化工作,最主要的是判断程序的类型以及初始化启动器和监听器。在这个过程中加载整个应用程序中的spring.factories文件,将文件内容放到缓存对象中,方便后续获取
  2. SpringApplication对象创建完成后,执行run()方法,完成程序的启动。该过程中最主要的有两个方法,第一个是prepareContext(),第二个是refreshContext(context),这两个关键步骤完成了自动装配的核心功能。
  • prepareContext()方法中主要完成了上下文对象的初始化操作,一些属性、环境配置的设置。在整个过程中有个非常重要的方法,叫load(),它主要完成了一件事——将当前启动类作为一个beanDefinition注册到registry中,方便后续在进行BeanFactoryPostProcessor调用执行的时候,找到对应的主类,来完成@SpringBootApplication,@EnableåAutoConfiguration等注解的解析工作。
  • 在refreshContext()方法中会进行整个容器的刷新,该过程会调用Spring中refresh()方法,refresh中有13个关键的方法,来完成整个程序的启动。在自动装配过程中,会调用invokeBeanFactoryPostProcessor()方法,主要对ConfigurationClassPostProcessor类的处理,在执行postProcessBeanDefinitionRegistry的时候会解析处理各种注解,包含@PropertySource,@ComponentScan,@ComponentScans,@Bean,@Import等注解。
  • 在解析@Import注解的时候,会有一个getImports的方法,从主类开始递归解析注解,把所有包含@Import的注解都解析到,然后在processImport方法中对Import的类进行分类,此处主要识别的时候AutoConfigurationImporSelect归属于ImportSelect的子类,在后续过程中会调用deferredImportSelectorHandler中的process方法,来完成EnableAutoConfiguration的加载。
上次更新: 2025/04/23, 16:23:16
Spring中事务的属性
Spring中Bean的创建跟循环依赖

← Spring中事务的属性 Spring中Bean的创建跟循环依赖→

最近更新
01
面向切面跟自定义注解的结合
05-22
02
时间跟其他数据的序列化
05-19
03
数据加密与安全
05-17
更多文章>
Theme by Vdoing | Copyright © 2023-2025 EffectTang
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式