本篇文章纯干货, 无废话, 觉得写得不错请点个赞支持一下;
SpringBoot启动流程
一定要配合这个流程图来看; 流程图重点关注 new SpringApplication
的部分即可
https://www.processon.com/view/link/667833be05675d325a7cf3fb?cid=65af5516f97995074a6638e9
重点关注带序号的部分
public ConfigurableApplicationContext run(String... args) {
// 用于记录启动时间
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 配置headless属性,即“java.awt.headless”属性,默认为ture
// 即使没有检测到显示器,也允许其启动.对于服务器来说,是不需要显示器的,所以要这样设置.
configureHeadlessProperty();
// [1] 通过getSpringFactoriesInstances从spring.factories配置文件中加载SpringApplicationRunListener对象并缓存;
SpringApplicationRunListeners listeners = getRunListeners(args);
// [2] 发布【ApplicationStartingEvent】事件;
// 其实就是观察者模式, 遍历SpringApplicationRunListeners持有的所有Listener
// 调用他们的 accept 方法, 将事件类型作为参数传进去, Listener 根据事件类型判断自己要不要处理;
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 封装 String[] args 参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// [3] 创建 Environment, 配置属性源
// 包括读取系统变量,环境变量,命令行参数,默认变量以及配置文件(application.properties)等;
// 注意这些属性源是有优先级的
// 发布【ApplicationEnvironmentPreparedEvent】事件
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 配置spring.beaninfo.ignore属性,默认为true,即跳过搜索BeanInfo classes.
configureIgnoreBeanInfo(environment);
// 控制台打印SpringBoot的bannner标志
Banner printedBanner = printBanner(environment);
// [4] 根据不同类型创建不同类型的spring applicationContext容器
// servlet环境下,创建的是AnnotationConfigServletWebServerApplicationContext容器
context = createApplicationContext();
// [5] 为刚创建的ApplicationContext容器对象做一些初始化工作,准备一些容器属性值等;
// 重点关注 应用初始化器的初始化方法, 添加BeanFactoryPostProcessor, 添加启动类BeanDefinition
// 1)设置AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
// 2)postProcessApplicationContext, 对容器做一些相关的后置处理,实现类重写这个方法
// 3)应用ApplicationContextInitializer的初始化方法,
// 5)从context容器中获取beanFactory,并向beanFactory中注册一些必要的单例bean,比如applicationArguments,printedBanner(已经打印过的Banner)
// 6) 添加两个BeanFactoryPostProcessor, 一个用于将Bean设置为默认懒加载
// 一个用于调整 PropertySource 的顺序;
// 7) 生成启动类的 BeanDefinition, 放到容器中;
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// [6] 刷新容器
refreshContext(context);
// [7] 执行刷新容器后的后置处理逻辑,注意这里为空方法, 是提供给SpringApplication子类的扩展
afterRefresh(context, applicationArguments);
// [8] 发布ApplicationStartedEvent事件,标志spring容器已经彻底准备好;
listeners.started(context);
// [9] 从容器中取出所有ApplicationRunner和CommandLineRunner
// 应用他们的 run 方法, 先 ApplicationRunner 再CommandLineRunner
// 可以在这里进行业务逻辑的一些初始化操作
callRunners(context, applicationArguments);
}
// ......
}
// refreshContext 来到 AbstractApplicationContext 的 refresh方法
public void refresh(){
// 刷新前做准备工作,比如初始化一些属性设置,属性合法性校验和保存容器中的一些早期事件等
prepareRefresh();
// [1] 从ApplicationContext获取 BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// [2] 对bean factory进行配置,配置bean factory的类加载器,后置处理器, 给beanFactory一些属性设置默认值
prepareBeanFactory(beanFactory);
// [3] 一个扩展点, 子类可以通过重写这个方法在BeanFactory准备完成后做进一步的设置;
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess =
this.applicationStartup.start("spring.context.beans.post-process");
// [4] 执行所有 BeanFactoryPostProcessor 的 postProcess 方法
// 里面有一个很重要的 ConfigurationClassPostProcessor
// 它是一个 BeanDefinitionRegistryPostProcessor
// 配置类就是在这里解析的, 这里会生成所有 Bean 的 BeanDefinition
// 注意, 会先执行 BeanDefinitionRegistryPostProcessor(触发时机:bean定义注册之前)
// 再执行 BeanFactoryPostProcessor(触发时机:bean定义注册之后bean实例化之前)
invokeBeanFactoryPostProcessors(beanFactory);
// [5] 实例化BeanPostProcessor, 添加到容器
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 初始化国际化MessageSource相关的组件,比如消息绑定,消息解析等
initMessageSource();
// [6] 初始化事件广播器, 发布 ApplicationListner 所感兴趣的那些事件
initApplicationEventMulticaster();
// [7] 不同实现类有不同的实现, Servlet 应用环境下, 这里是创建内嵌的tomcat容器
onRefresh();
// [8] 向事件发布器注册实现了ApplicationListener接口的监听器
registerListeners();
// [9] 初始化所有剩余的单例bean。这一步非常重要,绝大部分的bean都在这里实例化与初始化,
// bean post processor会在这里调用。
finishBeanFactoryInitialization(beanFactory);
// 发布ContextRefreshedEvent事件
finishRefresh();
}
配置类处理过程
pring Boot 会自动发现并注册所有在类路径下定义的 BeanFactoryPostProcessor
,包括那些通过 @Configuration
类、@Component
扫描或直接在 META-INF/spring.factories
文件中定义的。
在 SpringBoot 启动的 Refresh 阶段, 对 BeanFactory 进行一些属性默认设置后, 就会来到 invokeBeanFactoryPostProcessors
方法, 这个方法会应用 BeanDefinitionRegistryPostProcessor
和 BeanFacotryPostProcessor
的 postProcess 方法;
这其中就有专门处理配置类的ConfigurationClassPostProcessor
;
它是一个BeanDefinitionRegistryPostProcessor
;
配置类被处理后, 会被转换为一个ConfigurationClass
对象, 这个对象保存了配置类的信息;
配置类分类
配置类可以分为两种, Full 和 Lite, @Configuration注解修饰的是 Full;
没有 @Configuration 注解修饰, 但有 @ComponentScan, @Import, @Bean方法的是 Lite;
他们都被认为是配置类, 都会被 ConfigurationClassPostProcessor
处理;
处理过程
- SpringBoot 应用启动时, 在 PrepareContext 阶段将启动类的 BeanDefinition 放到了容器中;
- 总的过程是: 以处理启动类为开头, 去不断添加新的 BeanDefinition, 找到新的配置类, 处理新的配置类; 从而完成对所有配置类的处理;
- 创建
ConfigurationClassParser
, 在一个循环中去处理启动类, 启动类可以配置多个, 循环去处理他们; - 在循环内, 拿到待处理的一个启动类, 调用
parse
方法去处理这个配置类, 然后;
parse
方法内经过一系列调用, 先处理@ComponentScan
注解, 进行包扫描, 将扫描得到的 BeanDefinition 保存到容器中; 如果是配置类的 BeanDefinition, 将直接调用parse
方法, 进行处理;- 继续说
parse
方法的处理逻辑: 处理@Import
注解
parse
方法内调用processImports
处理@Import
注解,
- 遍历
@Import
注解引入的类对象;- 如果是
ImportBeanDefinitionRegister
, 将其添加到ConfigurationClass
对象中; 但不会向容器添加;- 如果是
ImportSelector
对象, 会根据其返回的数组, 加载对应的类; 然后递归地调用processImports
去处理这些类; 其本身不会向容器添加;- 如果不是这两种类, 会将他作为一个配置类去处理; 所以
ImportSelector
返回的的数组对应的类会被添加到容器中;
- 处理 @Bean 注解, 将 @Bean 注解修饰的方法生成一个对应的
BeanMethod
, 添加到对应的ConfigurationClass
中; - parse方法结束; 调用
ConfigurationClassBeanDefinitionReader::loadBeanDefinitions
- 遍历这一轮生成的所有
ConfigurationClass
, 对其持有的所有ImportBeanDefinitionRegister
, 调用他们的注册方法 - 解析所有
ConfigurationClass
的BeanMethod, 生成对应的 BeanDefinition, 添加到容器中;
- 没有新的要处理的启动类, 循环结束;
- 就这样处理完所有的配置类后, 会对 @Configuration 修饰的类, 进行 CGLIB 动态代理, 创建代理类, 代理 @Bean 注解修饰的方法; 但是创建代理类时, 仍会保留原本的目标类, 从而保证 AOP, 事务等功能能正常运行;