JAVA中SpringBoot启动流程分析
作者:Cuzzz 发布时间:2021-07-03 03:57:05
一丶前言
在之前我们学习了SpringBoot自动装配如何实现的,我们总结了Spring IOC的底层原理。
但是我们还是不知道SpringApplication.run(主类.class, args)
到底做了哪些事情。本文将和大家一起看看SpringBoot启动的大致流程,探讨SpringBoot留给我们的扩展接口
二丶SpringBoot启动流程分析
上面是SpringBoot调用SpringApplication.run(主类.class, args)
启动的源码,源码并不复杂,整体流程大概如下
下面我们依据此图,看看这些步骤SpringBoot底层源码
1.获取SpringApplicationRunListener
实现类,包装成SpringApplicationRunListeners
SpringApplicationRunListener
是SpringBoot框架中的 * ,在SpringBoot启动到达对应阶段的时候,会回调starting
,started
等方法。
为什么SpringBoot不适应Spring 里面的ApplicationListener
昵,因为ApplicationListener
依赖于Spring容器,@EventListener
注解需要EventListenerMethodProcessor
这个BeanFactoryPostProcessor
扫描,将对应的bean和方法包装成ApplicationListener
注册到ApplicationContext
中(最终注册到ApplicationEventMulticaster
事件多播器中)对于ApplicationListener
类型bean则直接走注册到ApplicationContext
的流程,整个流程只有Spring 容器启动后才能进行,如果没有SpringApplicationRunListener
则开发者无法在SpringBoot启动对应阶段进行一些扩展逻辑的回调。
SpringApplicationRunListeners
可以看成是SpringApplicationRunListener
的门面(门面设计模式)
其使用List<SpringApplicationRunListener>
持有所有的SpringApplicationRunListener
,然后starting
等方法都是循环调用,集合中SpringApplicationRunListener
对应的方法
SpringBoot如何获取所有的SpringApplciationListener
这里将从META-INF/spring.factories
获取org.springframework.boot.SpringApplicationRunListener
定义的实现类全限定类名,然后反射调用构造方法(SpringApplication application, String[] args)
进行实例化。随后将根据@Order
或者 Ordered
接口定义的顺序进行排序,然后包装成SpringApplicationRunListeners
注意无法使用@Component注解 标注在SpringApplciationListener
注解上,来实现事件的监听,必须在META-INF/spring.factories
中定义,并且必须具备构造方法(SpringApplication application, String[] args)
。
EventPublishingRunListener
SpringApplication#addListeners
允许我们注册ApplicationListener
到SpringBoot中,然后EventPublishingRunListener
其内部会new 一个简单的事件多播器SimpleApplicationEventMulticaster
,在对应的SpringBoot启动阶段,推送事件。下面式如何注册ApplicationListener
注意这些ApplicationListener不会被注册到Spring上下文中,意味着不会响应Spring上下文推送的事件,除非这个ApplicationListener是一个Spring Bean 并且被Spring管理。
下图是EventPublishingRunListener
在SpringBoot启动的不同阶段,推送事件
2.SpringApplicationListeners#starting
没啥好说的,循环回调SpringApplicationRunListener#starting
方法
3.prepareEnvironment 根据项目选择Environment实现类,并实例化
在这一步,SpringBoot会根据类路径中的类选择一个Environment
并实例化,并且根据当前激活的配置,选择对应的配置文件,进行解析,并保存到Environment
中。下面是SpringBoot选择Environment的源码
那么SpringBoot是如何判断当前项目是什么应用类型昵?
其实根据类路径下是否具备指定的类,然后得到指定类型,一般我们都是servlet应用,会选择StandardServletEnvironment
4.SpringApplicationListeners#environmentPrepared
同2.SpringApplicationListeners#starting
5.createApplicationContext
根据类路径指定类推断使用什么ConfigurableApplicationContext
(一般servlet应用使用AnnotationConfigServletWebServerApplicationContext)然后实例化AnnotationConfigServletWebServerApplicationContext
AnnotationConfigServletWebServerApplicationContext#onRefresh
方法在Spring容器刷新后会被调用,这个方法将启动Tomcat内嵌服务器
6.prepareContext
这个方法主要会做以下操作
回调ApplicationContextInitializer#initialize
回调所有SpringApplicationRunListener#contextPrepared
将主类包装成BeanDefinition
,注册到Spring容器上下文中 回调所有SpringApplicationRunListener#contextLoaded
利用SpringApplicationRunListeners
回调SpringApplicationRunListener
,同2,不在赘述
6.1从META-INFO/spring.factories中拿所有ApplicationContextInitializer
然后回调initialize方法
在spring上下文refresh方法调用前,会回调initialize
方法
这里调用前还会判断ApplicationContextInitializer
定义的泛型,保证5这一步创建的上下文,符合泛型的要求
6.2 将主类包装成BeanDefinition
,注册到Spring容器上下文中
这一步非常重要,主类上的注解@SpringBootApplication
需要ConfigurationClassPostProcessor
解析,才能发挥@Import,@ComponentScan的作用,想要ConfigurationClassPostProcessor
处理主类的前提是主类的BeanDefinition需要在Spring容器中。
也就是说SpringBoot的自动装配,和扫描包路径下的Spring 组件的前提是,主类的BeanDefinition在Spring容器中
这里的BeanDefinitionRegistry,其实就是来自5这一步的ApplicationContext,一般来说AnnotationConfigServletWebServerApplicationContext
内部持有了一个DefaultListableBeanFactory
,DefaultListableBeanFactory
是BeanDefinitionRegistry
的实现类,其底层使用一个ConcurrentHashMap
维护,key是bean的名称,value是对应的BeanDefinition
当资源是一个Class
的时候,会使用AnnotatedBeanDefinitionReader
读取Class
对象,生成BeanDefinition
这一步还支持xml的方式
7.回调SpringApplicationRunListener#contextLoaded
同2
8.刷新Spring容器上下文
《Spring源码学习笔记12——总结篇IOC,Bean的生命周期,三大扩展点》这篇博客做了详细的分析
这里会进行自动装配和包路径扫描注册BeanDefinition,然后实例化单例bean
9.回调SpringApplicationRunListener#started
同2
10.callRunners
从spring容器中拿到ApplicationRunner,和CommandLineRunner调用run方法
三丶SpringApplication,ApplicationContext,BeanFactory 三平面
我们将SpringApplication看作是SpringBoot平面,ApplicationContext看作是Spring平面,BeanFactory看作是Bean工厂平面,SpringBoot启动到触发spring容器刷新,然后触发BeanFactory实例化所有单例,非懒加载bean的流程如下
来源:https://www.cnblogs.com/cuzzz/p/17060884.html


猜你喜欢
- 废话不多说了额,直接给大家贴代码了,具体代码如下所示:/** * 下载指定路径的文件,并写入到指定的位置 * &
- 一 前言springboot 额外的特色是提供了后台应用监控,可以通过 HTTP 或者 JMX的方式管理监控应用,本文主讲HTTP方式;其主
- 开发设计搞了一个带圆形进度的进度条,在GitHub上逛了一圈,发现没有,自己撸吧。先看界面效果:主要思路是写一个继承ProgressBar的
- SQLite分析 SQLite是轻量级的、嵌入式的、关系型数据库,
- 没有阿里云数据库的可以买个最便宜的,我是新用户9.9元买了一个1.买到后点击左上角的工作台2.3. 4. 5.6.7.8
- JUC包(java.util.concurrent)中提供了对定时任务的支持,即ScheduledExecutorService接口。本文对
- 一、概念哈希算法(hash algorithm):是一种将任意内容的输入转换成相同长度输出的加密方式,其输出被称为哈希值。哈希表(hash
- springboot 启动项目打印接口列表环境springboot 2.3.2.RELEASE修改配置文件logging: le
- 使用后台返回验证码图片,验证码存到session中后端实现校验,前端只展示验证码图片。本篇用SpringBoot Thymeleaf实现验证
- 因为目前所用mybatis-plus版本为3.1.1,感觉是个半成品,所有在实体类上的注解只能支持单表,没有一对一和一对多关系映射,且该功能
- 文档中的设置有序或无序列表是一种反应内容上下级关系或者内容相同属性的方式,与单纯的文字叙述相比,它能有效增强文档内容的条理性,突出重点。因此
- 0x01 新建SpringBoot项目1. 新建maven工程ps:在上一教程的基础上操作,就不用新建项目了,请参考文章:SpringBoo
- 本文实例为大家分享了java实现递归菜单树的具体代码,供大家参考,具体内容如下1.表结构SET FOREIGN_KEY_CHECKS=0;-
- 一.线程池简介线程池的概念线程池就是首先创建一些线程,它们的集合称为线程池,使用线程池可以很好的提高性能,线程池在系统启动时既创建大量空闲的
- 本文实例讲述了C#基于NPOI生成具有精确列宽行高的Excel文件的方法,是非常具有实用价值的技巧分享给大家供大家参考。具体方法如下:。一、
- 本文实例讲述了C#中timer定时器用法。分享给大家供大家参考。具体如下:下面的代码通过Timer定时器每隔1000毫秒(1秒)触发一次事件
- 题目:将一个数组逆序输出。代码:import java.util.*;public class lianxi31 {public stati
- 引导语上一小节我们学习了 Socket,本文我们来看看服务端套接字 API:ServerSocket,本文学习完毕之后,我们就可以把客户端
- 前言前面我们已经实现了服务的注册与发现(请戳:SpringCloud系列——Eureka 服务注册与发现),并且在注册中心注册了一个服务my
- 继承的概念继承是面向对象编程中的一个概念,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。子类在继承父类的同时也