Springboot启动原理
一、 开始
启动一个springboot项目,最简单的就是配置一个springboot启动类,然后运行即可
@SpringBootApplication
public class SpringBoot {
public static void main(String[] args) {
SpringApplication.run(SpringBoot.class, args);
}
}
二、@SpringBootApplication原理解析
1. @SpringBootApplication组成
(1) @EnableAutoConfiguration(核心)
在启动时会自动导入“自动配置”AutoConfigurationlmportSelector类,该类会将所有符合条件的 @Configuration 配置都进行加载
(2)@SpringBootConfiguration
等价于@Configuration,将这个类标记为配置类会被加载到容器中
(3)@ComponentScan
自动扫描并加载符合条件的Bean
2. run(SpringBoot.class, args)原理解析
当执行run方法后,会进行四个阶段
(1)服务构建
首先要把传入的“资源加载器、主方法类”记录在栈内存中
然后我会逐一判断对应的服务类是否存在来确定web服务的类型(默认Servlet,Reactive,None)
- 基于Servlet的web服务有 Tomcat,响应式非阻塞服务Reactive(spring-webflux),啥也不是的None
去读取所有META-INF/spring.factories文件中的“注册初始化”(BootstrapRegistryinitializer)、“上下文初始化”(ApplicationContextinitializer)和“监听器”(ApplicationListener)这三类配置
接下来会通过“运行栈”stackTrace判断出main方法所在的类(启动类本身)
(2)环境准备
- new一个后续会陆续使用到的“启动上下文”(BootstrapContext)
- 同时逐一调用刚刚加载的“启动注册初始化器”BootstrapRegistrylnitializer中的初始化initialize方法
- 接下来将"java.awt.headless”这个设置改为true,表示缺少显示器、键盘等输入设备也可以正常启动
- 启动“运行监听器”(SpringApplicationRunListeners)
- 发布“启动”事件,获取并加载sping-boot工程spring gactories配置文件中的EventPublishingRunListener,从而会将监听器引入,从而我们可以通过监听事件在启动流程中加入自定义逻辑
- 通过prepareEnvironment方法“组装启动参数”
- 第一步 构造一个“可配置环境”ConfigurableEnvironment,根据不同的web服务类型会构造不同的环境,同样默认servlet
- 构造之后会加载很多诸如“系统环境变量”systemEnvironment,“jvm系统属性” systemProperties等在内的4组配置信息,把这些配置信息都加载到一个叫做propertySources的内存集合
- 通过“配置环境” configureEnvironment方法将我们启动时传入的环境参数args进行设置,列如启动时传入的诸如“开发/生产”环境配置等都会在这一步进行加载
- 同时在propertySources集合的首个位置添加一个值为空的配置内容“configurationProperties”
- 发布“环境准备完成”这个事件会被监听器监听后会进行相应处理,例如“环境配置后处理监听器”EnvironmentPostProcessorApplicationListener会去加载spring.factories配置文件中“环境配置后处理器”EnvironmentPostProcessor(“监听器”通过观察者模式设计是逐一“串行”执行,不是“异步并行”,需要等待所有监听器都处理完之后才去执行后续的逻辑) - 通过二次更新保证匹配,紧接着将Spring.beaninfo.ignore"设为true,同时打印banner图
(3)容器搭建(内部的属性、集合以及配套功能的结构体ApplicationContext)
通过createApplicationContext来创建容器
- 单先根据服务类型创建"容器"ConfigurableApplicationContext,创建“注解配置的Servlet-Web服务容器”Annotationconfig ServletWebServerApplicationContext
- 构造存放和生产我们bean实例的"Bean工厂”DefaultListableBeanFactory
- 用来解析@Component、@ComponentScan等注解的“配置类后处理器”ConfigurationClassPostProcessor
- 用来解析@Autowired、@Value、@lnject等注解的“自动注解Bean后处理器”AutowiredAnnotationBeanPostProcessor等在内的属性对象 - 通过prepareContext方法对容器中的部分属性进行初始化
- 先用postProcessApplicationContext方法设置"Bean名称生成器"、“资源加载器”、"类型转换器"等
- 执行之前我们加载进来的“上下文初始化”ApplicationContextinitializer(默认7个),实现容器ID、“警告自志处理、日志监听 - 发布“容器准备完成”监听事件,陆续为容器注册“启动参数、"Banner”、"Banner引用策略”和“懒加载策略”等等
- 通过Bean定义加载器将“启动类”在内的资源加载到“Bean定义池”BeanDefinitionMap中,以便后续根据Bean定义创建"Bean对象"
- 发布 “资源加载完成”事件
(4)填充容器
- 生成自身提供的Bean对象,与自定义的所有Bean对象,并放在“容器”器官中 ==(自动装配)
(12个小步骤 - Bean的生命周期管理,构造和启动一个web服务器)
第一步、通过prepareRefresh方法,在己有的“系统环境”基础上,准备servlet相关的环境Environment,其他的环境配置在“环境准备”中已经注册完成,通过“初始化属性资源” initServletPropertySources方法,对"Servlet初始化参数” servletContextInitParams和servletConfiginitParams进行赋值,然后通过validateRequiredProperties方法,检验是否有必填的环境变量,可以在自定义“初始化属性资源”initPropertySources方法中,通过setRequiredProperties將某些环境变量设置为必填,最后,完成监听器和事件初始化之后,环境准备就完成了
第二步、通过obtainFreshBeanFactory和prepareBeanFactory方法,在获取容器同时在使用 BeanFactory 之前进行一些准备工作,由于Springboot选择了ServletWebServerApplicationContext作为容器,在之前步骤已经构造好beanFactory了,所以obtainFreshBeanFactory中不进行任何处理 - 发布“启动完成事件的同时”,会回调自定义实现的Runner接口,来处理一些执行后定制化需求