Loading [MathJax]/jax/input/TeX/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Spring 全家桶之 Spring Boot 2.6.4(九)- 启动流程解析

Spring 全家桶之 Spring Boot 2.6.4(九)- 启动流程解析

作者头像
RiemannHypothesis
发布于 2022-09-26 07:57:16
发布于 2022-09-26 07:57:16
85400
代码可运行
举报
文章被收录于专栏:ElixirElixir
运行总次数:0
代码可运行

“Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。”

一、Debug Spring Boot 启动流程

创建工程spring-boot-fundamental,只添加基本依赖

Debug启动流程,在SpringApplication.run(AppApplication.class, args)这一行打上断点

创建SpringApplication对象

首先会创建SpringApplication对象

调用包含有ResourceLoader和Class<?>... 两个参数的构造函数创建对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

primarySources包含了主配置类,这句代码是判断传入的primarySources是否为空,如果主配置类存在就一定不为空,并将这些Class存储到LinkedHashSet集合中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.webApplicationType = WebApplicationType.deduceFromClasspath();

判断当前应用是不是Web应用

debug到这个方法中去

判断当前应用是一个Web应用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.bootstrapRegistryInitializers = new ArrayList<>(
      getSpringFactoriesInstances(BootstrapRegistryInitializer.class));

就是从spring.factories配置文件中获取所有的自动配置类

getSpringFactoriesInstances方法就是从配置文件中获取指定的配置类,根据传入的类型

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.bootstrapRegistryInitializers = new ArrayList<>(
      getSpringFactoriesInstances(BootstrapRegistryInitializer.class));

这句代码就是获取所有的BootstrapRegistryInitializer配置类,将其实例化并放入一个ArrayList中,赋值给bootstrapRegistryInitializers属性

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

这句代码就是设置initializers属性,通过getSpringFactoriesInstance获取配置文件中所有的ApplicationContextInitializer并实例化放入集合中作为initializers属性的值

Debug可以看到放置了这8个initializer到集合中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

setListeners也是通过调用getSpringFactoriesInstance获取配置文件中所有的ApplicationListener类并实例化放入listeners属性的集合中 debug查看放入多少个listener

deduceMainApplicationClass()方法是决定哪个程序是主程序

再往下进行debug

确定了主程序

到此,整个SpringApplication对象创建完成了

创建SpringApplication大概有这几步:

  1. 保存主配置类
  2. 判断是否为Web应用
  3. 从类路径下找到META-INF/spring.factorues配置所有的ApplicationContextInitializer,然后保存起来

执行run()方法

在SpringApplication对象创建完成之后,开始执行run()方法;重新启动Debug,进入run方法

此时SpringApplication对象已经创建好,run方法中的流程就是Spring Boot启动的流程。

Step Over第一句289行代码只是为了标记一个起始时间

这里是将创建SpringApplication对象时从配置文件中获取所有的BootstrapRegistryInitializer配置类,这里将列表遍历,但是列表为空,返回一个默认的DefaultBootstrapContext

第291行声明了一个容器,接下来所有的代码就是往这个容器中注册组件,最终返回这个容器

context容器中注入了哪些组件? 292行配置headless属性为True,与awt应用相关

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SpringApplicationRunListeners listeners = getRunListeners(args);

Step Into 进入到 getRunListeners方法

这里又调用了getSpringFactoriesInstances方法,这个方法的作用就是从类路径下的spring.factoies中获取指定的类,这里要获取的是所有的SpringApplicationRunListener类

这里获取了一个EventPublishRunListener

继续Step Over到294行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
listeners.starting(bootstrapContext, this.mainApplicationClass);

这里调用了starting方法,参数为默认的boostrapContext和SpringAppication中的主程序既AppApplication,进入starting方法

这里是循环启动这些监听器

在296行上打断点,重启启动debug模式;Step Over进入到prepareEnvironment方法中,也就是准备环境

该方法中先是创建了一个environment,创建环境之后回调listeners,表示环境准备完成

第299行是打印banner也就是启动应用时控制台出现的Spring图标,这个图标是可以自定义的

Step Into 进入 300行的createApplicationContext方法

再次step into进入到create方法

这里根据WebApplication是Servlet类型返回了AnnotationConfigServletWebServerApplicationContext容器

第301行给容器设置启动属性,设置了一个DefaultApplicationStartup

下一行第302行的作用是准备上下文环境,进入到该方法中;这个方法中对容器做了写配置

set环境,注册一些后置处理器,进入applyInitializers方法中

这个方法就是遍历SpringApplication中initializers中所有的initializer,然后调用initialize方法;遍历完成之后来到第382行

进入到contextPrepared方法中

这里就是回调所有的listener的contextPrepared方法

回到prepareContext方法,step over到第389行

这里就是注册命令行参数,并且将banner也注册进来;Step Over到第398行

这里做了判断,然后对容器进行了set操作;来到第406行

这里获取主程序类,然后判断是否为空

preparedContext方法最后一步,所有的listener回调contextLoad方法

至此,容器准备完毕。

step over回到run方法的第303行

step into 进入到refreshContext方法

进入refresh方法

再次进入refresh方法

再次进入refresh方法

这里就是IOC容器的容器初始化方法

实例化所有的单实例Bean,这就是refreshContext方法的作用

回到run方法,来到afterRefresh方法

step into afterRefresh方法

afterRefresh方法为空

再往下的代码是记录时间和日志

step over 到310行

进入callRunnser方法

这方法第753,754行是context容器获取ApplicationRunner和CommandRunner两个类型的Bean

再往下,就是进行回调,最后返回IoC容器。

二、Spring Boot 启动流程总结

run方法启动流程:

  • 准备环境
    • 执行ApplicationContextInitializer.initialize()
    • 监听器SpringApplicationRunListener回调contextPrepared
    • 记载主配置类定义信息
    • 监听器SpringApplicationRunListener回调contextLoaded
  • 刷新启动IoC容器
    • 扫描加载所有容器中的组件
    • 包括从META-INF/spring.factories中获取的所有EnableAutoConfiguration自动配置类
  • 回调容器中所有的ApplicationRunner、CommandLineRunner的run方法
  • 监听器SpringApplicationRunListener回调finished方法

三、Spring Boot 事件监听机制

在启动流程中,有几个监听器非常重要

  • ApplicationContextInitializer
  • SpringApplicationRunListener
  • ApplicationRunner
  • CommandLineRunner 可以自定义监听器实现这些提供的监听器,通过启动应用看这些监听器在什么时候运行

实现自定义的ApplicationContextListener

ApplicationContextListener接口中包含了一个initialize()方法

自定义监听器HalloApplicationContextListener并实现ApplicationContextListener接口,泛型为ConfigurableApplicationContext,既监听IOC容器的启动。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class HalloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("自定义的" + this.getClass().getName() + "运行了,IoC容器:" + applicationContext);
    }
}

实现自定义的SpringApplicationRunListener

SpringApplicationRunListener接口中的方法都是定义为defualt

实现自定义的HalloSpringApplicationRunListener时不是必须要实现所有的方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class HalloSpringApplicationRunListener implements SpringApplicationRunListener {

    @Override
    public void starting(ConfigurableBootstrapContext bootstrapContext) {
        System.out.println(this.getClass().getSimpleName() + "启动了......");
    }

    @Override
    public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
        String OSName = (String)environment.getSystemProperties().get("获取到的系统名称为:" + "os.name");
        System.out.println(this.getClass().getSimpleName() + "环境准备好了,获取到的系统名称为:" + OSName);
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println(this.getClass().getSimpleName() + "IOC容器准备好了.....");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println(this.getClass().getSimpleName() + "容器加载完成.....");
    }

    @Override
    public void started(ConfigurableApplicationContext context, Duration timeTaken) {
        System.out.println(this.getClass().getSimpleName() + "容器启动完成,耗时:" + timeTaken);
    }

}

实现自定义的ApplicationRunner

ApplicationRunner接口只只包含了一个run()方法

自定义HalloApplicationRunner实现ApplicationRunner接口

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class HalloApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(this.getClass().getSimpleName() + "运行ing....");
    }
}

实现自定义的CommandLineRunner

CommandLineRunner接口中只有一个run()方法

自定义HalloCommandLineRunner类实现CommandLineRunner

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class HalloCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println(this.getClass().getSimpleName() + "运行ing,传入的参数为:" + Arrays.asList(args));
    }
}

配置自定义组件

HalloCommandLineRunner和HalloApplicationRunner需要通过添加@Component注解注册到容器中

HalloApplicationContextInitializer和HalloSpringApplicationRunListener 需要配置META-INF/spring.factories配置文件中,在resources目录下新建META-INF/spring.factories配置文件,而配置文件的具体内容可以参看源码中spring.factories中的配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.lilith.listener.HalloApplicationContextInitializer
# Application Listeners
org.springframework.boot.SpringApplicationRunListener=\
com.lilith.listener.HalloSpringApplicationRunListener

测试监听器是否生效

启动应用

控制台报错缺少一个有参构造器

HalloSpringApplicationRunListener中增加一个有参构造器,可以参考SpringApplicationRunListener的另一个实现类EventPublishingRunListener

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public HalloSpringApplicationRunListener(SpringApplication application, String[] args) {
}

再次启动应用

控制台打印出了监听器中输出的信息

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-03-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Spring Boot 框架整体启动流程详解
main方法内部再调用SpringApplication.run(SpringBootDemoApplication.class);
阿提说说
2023/10/16
6760
Spring Boot 框架整体启动流程详解
SpringBoot启动流程大揭秘
日常开发中采用的是开源的若依框架,也就是SpringBoot框架,那么什么是SpringBoot框架呢? SpringBoot是一款开箱即用框架,提供各种默认配置来简化项目配置,让我们的Spring应用变的更轻量化、更快的入门,在主程序执行main函数就可以运行,也可以打包你的应用为jar并通过使用java -jar来运行你的Web应用。 使用SpringBoot只需很少的配置,大部分的时候直接使用默认的配置即可。同时后续也可以与Spring Cloud的微服务无缝结合。
六月的雨在Tencent
2024/03/29
1440
SpringBoot启动流程大揭秘
从SpringBootApplication谈谈Spring Boot启动时都做了哪些事?
Spring Boot 极大的减少了我们Spring项目开发的工作量,很多的配置文件往往都不需要编写了,只需要引入对应的starter,就可以完成配置实例的自动装配。那么,在Spring Boot项目执行时,到底都做了哪些事呢?
架构探险之道
2020/01/13
1.7K0
从SpringBootApplication谈谈Spring Boot启动时都做了哪些事?
【原创】Spring Boot终极篇《下》
在前面文章中,我们聊过SpringBoot是如何解决依赖配置,以及如何实现自动装配的。今天我们继续来聊Springboot的启动流程。
田维常
2020/09/22
6000
【原创】Spring Boot终极篇《下》
可能是全网最全的SpringBoot启动流程源码分析(最新3.x版本)
静态辅助类,可用于运行使用默认配置(即我们添加的一系列注解)的指定源的 SpringApplication 。
JavaEdge
2020/05/26
1.9K0
可能是全网最全的SpringBoot启动流程源码分析(最新3.x版本)
SpringBoot启动配置原理
# SpringBoot启动配置原理 几个重要的事件回调机制 配置在META-INF/spring.factories ApplicationContextInitializer SpringApplicationRunListener 只需要放在ioc容器中 ApplicationRunner CommandLineRunner 启动流程: 1、创建SpringApplication对象 initialize(sources); private void initialize(Object[] sourc
程序员阿杜
2021/03/16
3390
玩转 Spring Boot 原理篇(启动机制源码剖析)
玩转 Spring Boot 集成篇(Actuator、Spring Boot Admin)
一猿小讲
2022/04/12
5890
玩转 Spring Boot 原理篇(启动机制源码剖析)
头秃系列,二十三张图带你从源码分析Spring Boot 启动流程~
Spring Boot 专栏已经写了五十多天了,前面二十章从基础应用到高级整合避重就轻介绍的都是工作、面试中常见的知识点。
Bug开发工程师
2020/12/15
2.2K0
头秃系列,二十三张图带你从源码分析Spring Boot 启动流程~
spring boot启动过程
springboot的启动类我们一般都会加上SpringBootApplication注解,其实他是几个注解的集合
earthchen
2020/09/24
2.1K0
SpringBoot之旅-启动原理及自定义starter
SpringBoot的一大优势就是Starter,由于SpringBoot有很多开箱即用的Starter依赖,使得我们开发变得简单,我们不需要过多的关注框架的配置。
烂猪皮
2023/09/04
3300
SpringBoot之旅-启动原理及自定义starter
SpringBoot源码篇(二)启动过程
对于注解@SpringBootApplication不会再进行说明,重点是 SpringApplication.run(App.class);,从SpringMvc到Spring Boot,通过主程序便可以启动web服务,所以在这个方法中,有启动tomcat的步骤,但初始化spring容器和servlet容器是否还是和tomcat+SpringMvc一样?
用针戳左手中指指头
2022/11/16
5780
SpringBoot源码篇(二)启动过程
springboot源码解析详细版
来看下 createApplicationContext() 方法的源码: 作用:
全栈程序员站长
2022/08/14
7880
Spring Boot(6) 原理和启动流程四、ApplicationContextInitializer、ApplicationContextAware、ApplicationContext、App
SpringBoot是一个快速开发框架,目的是解放java程序猿的生产力,提高开发效率。主要特点: 1、整合依赖:通过Maven,快速的将一些常用的第三方依赖整合。 2、简化配置:简化XML配置,全部采用注解形式。 3、集成web容器:内置Http服务器(Jetty和Tomcat),最终以java应用程序进行执行。 简化Spring应用的创建、运行、调试、部署的工作,使用它可以做到专注于Spring应用的开发,而无需过多关注XML的配置。
黄规速
2022/04/14
1.7K0
Spring Boot(6) 原理和启动流程四、ApplicationContextInitializer、ApplicationContextAware、ApplicationContext、App
SpringApplication.run(MyApplication.class, args)运行流程源码分析[通俗易懂]
在启动时会加载三个jar将其对应的spring.factories工厂文件的接口实现类到MultiValueMap集合当中,并将对应加载器作为key,接口实现类作为value放到缓存当中
全栈程序员站长
2022/09/30
1.4K0
SpringApplication.run(MyApplication.class, args)运行流程源码分析[通俗易懂]
Spring Boot:最全SpringBoot启动流程原理分析(全网最全最完善)
我们启动一个springboot项目,最简单的就是配置一个springboot启动类,然后运行即可
Freedom123
2024/03/29
13K0
Spring Boot:最全SpringBoot启动流程原理分析(全网最全最完善)
SpringBoot 启动配置原理
2. 先创建一个空项目,然后 oy-spring-boot-starter(用 maven 创建)和 oy-spring-boot-starter-autoconfigurer(spring Initializr 创建)
OY
2022/03/17
3050
SpringBoot 启动配置原理
从源码的角度详细分析SpringBoot启动流程
SpringBoot项目的启动流程是很多面试官面试中高级Java程序员喜欢问的问题。这个问题的答案涉及到了SpringBoot工程中的源码,也许我们之前看过别的大牛写过的有关SpringBoot项目启动流程的文章,但是自己没有去研究一遍总是会记忆不深刻。有句话叫做“纸上来得终觉浅,绝知此事要躬行”,我觉得说得非常在理。底层的东西,也只有自己深入研究过一遍甚至好几遍源码才能彻底搞懂并记忆牢固。下面笔者来带领大家详细分析SpringBoot启动过程中到底做了哪些事情,把本文仔细看完了,面对面试官问的有关SpringBoot启动过程做了哪些工作的面试题就迎刃而解了!
用户3587585
2022/04/14
1.2K0
SpringBoot2核心技术-原理解析
xxxAutoConfiguration --- 容器中放入组件 ---- 绑定xxxProperties ----** 配置项
yuanshuai
2022/08/22
2800
SpringBoot2核心技术-原理解析
Springboot之启动原理
SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏。所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面纱,让它不在神秘。
用户3467126
2019/08/20
6010
Springboot之启动原理
Spring Boot启动过程分析
首先贴一张很不错的图,SpringBoot启动结构图,图片出自SpringBoot启动流程解析。 本文的分析基于Spring Boot 2.1.5,非Spring的代码只有下面这个启动main函数:
爱撸猫的杰
2020/03/08
1.2K0
Spring Boot启动过程分析
推荐阅读
相关推荐
Spring Boot 框架整体启动流程详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档