首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
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
86600
代码可运行
举报
文章被收录于专栏: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 原理篇(启动机制源码剖析)
玩转 Spring Boot 集成篇(Actuator、Spring Boot Admin)
一猿小讲
2022/04/12
6060
玩转 Spring Boot 原理篇(启动机制源码剖析)
可能是全网最全的SpringBoot启动流程源码分析(最新3.x版本)
静态辅助类,可用于运行使用默认配置(即我们添加的一系列注解)的指定源的 SpringApplication 。
JavaEdge
2020/05/26
2K0
可能是全网最全的SpringBoot启动流程源码分析(最新3.x版本)
SpringBoot源码学习(一)
工作中对于我们java开发者来说最经常使用的框架就是spring了,那么了解spring的基础原理对于我们的能力提升具有很大的好处。首先,作为框架首先它肯定还是从java基础演变而来,也就是说框架的代码都是基于我们日常使用的继承、多态已经各种设计模式的整合而抽取出来的一套规范。我们开发项目要想搭乘spring的快车就需要按规范做事,按规范开发。那么规范的熟悉程度就等价于项目质量的高低。当然框架的基础都是好的idea,好的idea不仅兼容一切还简单。这就和物理界的大一统理论一样。怎么做不重要,重要的是怎么兼容一切,因为做的办法会有无限种。实在无法实现也可以同缩小解的范围。废话不说了。让我们开启spring的流浪之旅。
写一点笔记
2020/09/10
6230
SpringBoot启动配置原理
# SpringBoot启动配置原理 几个重要的事件回调机制 配置在META-INF/spring.factories ApplicationContextInitializer SpringApplicationRunListener 只需要放在ioc容器中 ApplicationRunner CommandLineRunner 启动流程: 1、创建SpringApplication对象 initialize(sources); private void initialize(Object[] sourc
程序员阿杜
2021/03/16
3500
深入剖析Springboot启动原理的底层源码,再也不怕面试官问了!
Spring Boot的配置类;标注在某个类上,表示一个类提供了Spring Boot应用程序
全栈程序员站长
2022/09/06
6370
深入剖析Springboot启动原理的底层源码,再也不怕面试官问了!
SpringBoot启动流程大揭秘
日常开发中采用的是开源的若依框架,也就是SpringBoot框架,那么什么是SpringBoot框架呢? SpringBoot是一款开箱即用框架,提供各种默认配置来简化项目配置,让我们的Spring应用变的更轻量化、更快的入门,在主程序执行main函数就可以运行,也可以打包你的应用为jar并通过使用java -jar来运行你的Web应用。 使用SpringBoot只需很少的配置,大部分的时候直接使用默认的配置即可。同时后续也可以与Spring Cloud的微服务无缝结合。
六月的雨在Tencent
2024/03/29
1620
SpringBoot启动流程大揭秘
springboot源码解析详细版
来看下 createApplicationContext() 方法的源码: 作用:
全栈程序员站长
2022/08/14
8000
Spring Boot 框架整体启动流程详解
main方法内部再调用SpringApplication.run(SpringBootDemoApplication.class);
阿提说说
2023/10/16
7070
Spring Boot 框架整体启动流程详解
从SpringBootApplication谈谈Spring Boot启动时都做了哪些事?
Spring Boot 极大的减少了我们Spring项目开发的工作量,很多的配置文件往往都不需要编写了,只需要引入对应的starter,就可以完成配置实例的自动装配。那么,在Spring Boot项目执行时,到底都做了哪些事呢?
架构探险之道
2020/01/13
1.7K0
从SpringBootApplication谈谈Spring Boot启动时都做了哪些事?
头秃系列,二十三张图带你从源码分析Spring Boot 启动流程~
Spring Boot 专栏已经写了五十多天了,前面二十章从基础应用到高级整合避重就轻介绍的都是工作、面试中常见的知识点。
Bug开发工程师
2020/12/15
2.3K0
头秃系列,二十三张图带你从源码分析Spring Boot 启动流程~
Spring Boot自动配置原理,你必须懂
小伙伴们是否想起曾经被 SSM 整合支配的恐惧?相信很多小伙伴都是有过这样的经历的,一大堆配置问题,各种排除扫描,导入一个新的依赖又得添加新的配置。自从有了 Spring Boot 之后,咋们就起飞了!各种零配置开箱即用,而我们之所以开发起来能够这么爽,自动配置的功劳少不了,今天我们就一起来讨论一下 Spring Boot 自动配置原理,看完心里有个大概,不至于被面试官问的面红耳赤。
南风
2019/10/10
1.2K0
Spring Boot自动配置原理,你必须懂
Spring Boot 2.x 启动全过程源码分析(全)
上篇《Spring Boot 2.x 启动全过程源码分析(一)入口类剖析》我们分析了 Spring Boot 入口类 SpringApplication 的源码,并知道了其构造原理,这篇我们继续往下面分析其核心 run 方法。
Java技术栈
2018/08/17
2.5K0
Spring Boot 2.x 启动全过程源码分析(全)
SpringBoot的启动流程是怎样的?SpringBoot源码(七)
本篇接 SpringBoot内置的各种Starter是怎样构建的? SpringBoot源码(六)
源码笔记
2020/03/23
1.1K0
SpringBoot的启动流程是怎样的?SpringBoot源码(七)
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.8K0
Spring Boot(6) 原理和启动流程四、ApplicationContextInitializer、ApplicationContextAware、ApplicationContext、App
SpringBoot2核心技术-原理解析
xxxAutoConfiguration --- 容器中放入组件 ---- 绑定xxxProperties ----** 配置项
yuanshuai
2022/08/22
2880
SpringBoot2核心技术-原理解析
spring boot启动过程
springboot的启动类我们一般都会加上SpringBootApplication注解,其实他是几个注解的集合
earthchen
2020/09/24
2.1K0
Spring Boot:最全SpringBoot启动流程原理分析(全网最全最完善)
我们启动一个springboot项目,最简单的就是配置一个springboot启动类,然后运行即可
Freedom123
2024/03/29
14.9K0
Spring Boot:最全SpringBoot启动流程原理分析(全网最全最完善)
Springboot之启动原理
SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏。所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面纱,让它不在神秘。
用户3467126
2019/08/20
6100
Springboot之启动原理
Spring Boot启动过程分析
首先贴一张很不错的图,SpringBoot启动结构图,图片出自SpringBoot启动流程解析。 本文的分析基于Spring Boot 2.1.5,非Spring的代码只有下面这个启动main函数:
爱撸猫的杰
2020/03/08
1.2K0
Spring Boot启动过程分析
20-SpringBoot事件监听
③监听器:Listener,实现 java.util.EventListener 接口 的对象
Devops海洋的渔夫
2022/03/23
3140
20-SpringBoot事件监听
推荐阅读
相关推荐
玩转 Spring Boot 原理篇(启动机制源码剖析)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验