【视频笔记】史上最完整的Spring启动流程
springboot启动原理
首先需要有一个@SpringBootApplication注解的启动类。 这个注解本质上是由@EnableAutoConfiguration、@SpringBootConfiguration、@ComponentScan
这“三”个注解“连”起来构成。
@EnableAutoConfiguration是最为核心的,有了它之后,在启动时就会导入“自动配置”AutoConfigurationImportSelector类,这个类会将所有符合条件的@Configuration配置都进行加载。
而@SpringBootConfiguration等同于@Configuration,就是将这个类标记为配置类,会被加载到容器中。
最后@ComponentScan就是自动扫描并加载符合条件的Bean

其实,如果启动类中不需要增加配置内容,也不需要指定扫描路径,那可以使用@EnableAutoConfiguration替代@SpringBootApplication,也可以完成启动
注解完成后,我运行的起点就是SpringApplication run()这个方法拉,在run开始执行后,会经历如下四个阶段:
“服务构建”,“环境准备”,“容器创建”,“填充容器”

“服务构建”所说的“服务”,就是指springApplication本身,一个功能非常强大的Spring服务对象SpringApplication,这个阶段就是用一大堆零件把它组装出来
在构造方法里,首先要把传入的“资源加载器、主方法类”记录在内存中

然后springApplication会逐一判断对应的服务类是否存在,来确定web服务的类型,默认是Servlet,即基于Servlet的web服务,如tomcat;

还有响应式非阻塞服务REACTIVE,如spring-webflux;还有什么都不是用到NONE
确定了哪个web服务之后,然后就要加载初始化类了,我会去读取所有META-INF/spring.factories文件中的“注册初始化”、“上下文初始化”、“监听器”这三类配置

即BootstrapRegistryInitializer、ApplicationContextInitializer和ApplicationListener
在我体内没有默认的“注册初始化”配置,而spring-boot和spring-boot-autoconfigure这两个工程中配置了7个“上下文初始化”和8个“监听器”,这些配置信息会在后续的启动过程中使用到,当然大家也可以自定义这三个配置,只需要将其放到工程中的spring factories文件中,我就会将他们一并加载进来。
接下来会通过“运行栈”stackTrace判断出main方法所在的类,大概率就是启动类本身,后续过程会使用到

这样我的spring服务SpringApplication就构造完成了,然后就是调用它的run方法,进入“环境准备”阶段了
这个阶段的目的,是要给我即将诞生的“容器”器官做充足的养分准备。我会new一个后续会陆续使用到的“启动上下文”BootstrapContext
同时逐一调用刚刚加载的“启动注册初始化器”,bootstrapRegistryInitializer中的初始化initialize方法。(刚才提到了,体内并没有默认的BootstrapRegistryInitializer),
所以默认并不执行什么。接下来将“java.awt.headless”这个设置改为true,表示缺少显示器、键盘等输入设备也可以正常启动;
然后会启动我体内的“运行监听器” SpringApplicationRunListeners, 同时发布“启动”事件,它获取并加载spring-boot工程spring factories配置文件中的EventPublishingRunListener,他在启动时,也会将刚我们说的8个ApplicationListener都进行引入,这样大家就可以通过监听这些事件,然后在启动流程中加入自定义逻辑了。

接下来就要通过prepareEnvironment方法“组装启动参数”了。
首先第一步就是构造一个“可配置环境”ConfigurableEnvironment,根据不同的Web服务类型会构造不同的环境,同样默认是servlet,构造之后,会加载很多诸如“系统环境变量”systemEnvironment,“jvm系统属性”,systemProperties等在内的4组配置信息,把这些配置信息都加载到一个叫做propertySources的内存集合中,
这样后续使用到这些信息就无需重新加载了。这时也会通过‘配置环境’configureEnvironment方法将我们启动时传入的环境参数args进行设置。
例如,启动时传入的诸如“开发/生产”环境配置等都会在这一步进行加载;同时在propertySource集合的首个位置添加一个值为空的配置内容 “configurationProperties”后续会被使用
接下来就会发布“环境准备完成”这个事件,刚加载进来的8个Listener会监听到这个事件,其中的部分“监听器”会进行相应处理,诸如“环境配置后处理监听器”
EnvironmentPostProcessorApplicationListener会去加载spring.factories配置文件中“环境配置后处理器”EnvironmentPostProcessor,这里要注意“监听器”通过观察者模式设计,是逐一"串行"执行,并不是“异步”并行,需要等待所有监听器都处理完成之后,才会继续走后续的逻辑 ,将环境绑定到我体内之后,
剩下的就是考虑到刚创建的“可配置环境”在一系列过程中可能会有变化,进而做的补偿,通过二次更新保证匹配,紧接着将“spring.beaninfo.ignore”设为true
表示不加载Bean的元数据信息,同时打印我的Banner图,这样“环境准备”阶段就完成了。

然后就到“容器创建”阶段了。
我会将上一阶段准备好的各种养分进行组合,孵化我最最核心的“容器”器官。所谓“容器”,就是内部有很多奇奇怪怪属性、集合以及配套功能的结构体ApplicationContext, 也可以叫做“上下文”,当然叫做“容器”更好理解,我会通过createApplicationContext来创建容器,创建过程很简单,
首先根据服务类型创建“容器”ConfigurableApplicationContext,默认的服务类型是Servlet,所以创建的是“注解配置的Servlet-Web服务容器”,
即AnnotationConfigServletWebServerApplicationContext,在这个过程中,会构造诸如:存放和生产我们bean实例的“Bean工厂”, DefaultListableBeanFactory;
用来解析@Component、@ComponentScan等注解的“配置类后处理器” ConfigurationClassPostProcessor;
用来解析@Autowired、@Value、@Inject等注解的“自动注解Bean后处理器” AutowiredAnnotationBeanPostProcessor等在内的属性对象,

把他们都放入容器中后,就要通过prepareContext方法对容器中的部分属性进行初始化了

先用postProcessorApplicationContext方法设置“Bean名称生成器”, “资源加载器”、“类型转换器”等
接着就要执行之前我们加载进来的“上下文初始化”,ApplicationContextInitializer了,默认加载了7个,容器ID,警告日志处理、日志监听都是在这里实现的;
在发布“容器准备完成”监听事件之后,会陆续为容器注册“启动参数”、“Banner”、“Bean引用策略”和“懒加载策略”等等
之后通过Bean定义加载器将“启动类”在内的资源,加载到“Bean定义池” BeanDefinitionMap中,以便后续根据Bean定义创建“Bean对象”,然后发布一个“资源加载完成”事件,

我体内最核心的“容器”就创建完成拉

4、填充容器
我会在这一步中生成我自身提供的以及用户自定义的所有Bean对象,并且放在我刚刚创建好的“容器”器官中,这个过程也被称为“自动装配”,这个过程大体分为12个小步骤,这些小步骤中不但包含了我之前介绍过的“Bean生命周期”管理

同时还会构造和启动一个Web服务器,这样各位大哥就可以通过web方式来使用我拉
在这12步之后,发布“启动完成”事件的同时,会回调用户自定义实现的Runner接口,来处理一些执行后定制化需求

这样,完整的启动流程就介绍完了。

参考:史上最完整的Spring启动流程_哔哩哔哩_bilibili
浙公网安备 33010602011771号