springboot(2.4.1)源代码分析六springboot启动逻辑总结 置顶!
序
springboot的启动代码拉通看了一遍,逻辑还是很复杂,其中用到了事件驱动,除了springboot本身自带的监听器以外,还可以自定义一系列监听器,还是非常灵活,下面就springboot整个启动逻辑,记录一下关键点,方便后续回顾。下面图是run过程中的几个关键事件:
正文
下面直入正题,从最开始的new记录起:
1、new SpringApplication(primarySources)
- 根据classpath下的类,判断出WebApplicationType
- 从META-INF/spring.factories加载所有的BootstrapRegistryInitializer
- 从META-INF/spring.factories加载所有的ApplicationContextInitializer
- 从META-INF/spring.factories加载所有的ApplicationListener
- 从当前执行的栈帧,探测出执行main方法的mainApplicationClass
2、执行SpringApplication的run方法
这个方法就是springboot启动的核心方法
- 创建DefaultBootstrapContext,引导上下文是作为真正上下文创建前的引导类使用,在真正的上下文创建后这个对象会被释放。
- 调用所有BootstrapRegistryInitializer的initialize方法,初始化DefaultBootstrapContext,默认没有BootstrapRegistryInitializer,可以自定义引导上下文的初始化器。
- 设置java.awt.headless模式,默认为true,用于在缺失显示屏、鼠标或者键盘时的系统配置。
- 从META-INF/spring.factories加载所有的SpringApplicationRunListener,用于接收springboot启动时候的那几个事件,如上图中所示starting、environmentPrepared、contextPrepared、contextLoaded、started、running(ready)、failed
- 触发监听者的starting事件
- 创建ApplicationArguments,用来解析程序启动命令行参数
3、执行prepareEnvironment方法准备ConfigurableEnvironment应用环境
这个是run方法中的代码,单独拿出来写是因为很重要。
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
- 根据webApplicationType类型,创建对应的ConfigurableEnvironment,目前有三类,基于传统servlet的ApplicationServletEnvironment、基于reactive响应式web编程的ApplicationReactiveWebEnvironment,以及常规java程序ApplicationEnvironment。
- 加载配置源和Profiles
- 添加ConfigurationPropertySourcesPropertySource到配置源中,用来桥接 ConfigurationPropertySources类型的资源,ConfigurationPropertySources是什么类型?就是@ConfigurationProperties注解底层使用的封装,获取配置文件中的prefix,和注解对象的类成员变量,递归将配置属性赋值给类成员变量这一类型的资源。
- 执行SpringApplicationRunListener的environmentPrepared事件,这个事件的监听者有很多,其中有几个关键的监听者:
- EnvironmentPostProcessorApplicationListener,它又包含spring.factories文件中所有定义的EnvironmentPostProcessor对象,例如添加随机属性处理器、属性命名解析处理器、json配置处理器、以及核心的
ConfigDataEnvironmentPostProcessor
,这个类是用来判断Springboot运行环境处理器,判断springboot当前运行在哪个环境,参考spring.profiles.active配置。 - LoggingApplicationListener,初始化日志系统
- BackgroundPreinitializer,后台资源初始化监听,例如JacksonInitializer、ConversionServiceInitializer、ValidationInitializer等等
- DelegatingApplicationListener,委派监听,可以把启动事件广播到context.listener.classes配置的自定义监听器上面
- EnvironmentPostProcessorApplicationListener,它又包含spring.factories文件中所有定义的EnvironmentPostProcessor对象,例如添加随机属性处理器、属性命名解析处理器、json配置处理器、以及核心的
- 将defaultProperties移动到配置源的末尾,使其优先级最低
- 采用Binder,将spring.main配置的属性绑定到SpringApplication对象上,Binder用来绑定配置属性非常方便。
- EnvironmentConverter尝试对环境变量Environment进行转换,还是基于webApplicationType类型进行转换(因为最开始那个Environment就是基于webApplicationType类型创建的,不知道为啥还要在这里再判断一次)
- 再一次添加ConfigurationPropertySourcesPropertySource到配置源中(逻辑同上)
环境准备过后,确定了多个配置变量的获取优先级,这个很重要,如下所示,越前面的优先级越高:
- ConfigurationPropertySourcesPropertySource {name='configurationProperties'}:
- SimpleCommandLinePropertySource {name='commandLineArgs'}:命令行参数
- StubPropertySource {name='servletConfigInitParams'}:servlet初始化参数
- StubPropertySource {name='servletContextInitParams'}:ServletContext初始化参数
- PropertiesPropertySource {name='systemProperties'}:JVM的System系统属性
- OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}:操作系统的环境变量
- RandomValuePropertySource {name='random'}:随机生成的带random.*前缀的属性
下面两个是加载的classpath:/config/application.yml或者classpath:/config/application.properties,调试环境的spring.profiles.active=dev - OriginTrackedMapPropertySource {name='Config resource 'class path resource [application-dev.yml]' via location 'optional:classpath:/''}
- OriginTrackedMapPropertySource {name='Config resource 'class path resource [application.yml]' via location 'optional:classpath:/''}
4、执行prepareContext准备上下文
继续执行run中逻辑,中间有一些非核心逻辑,例如打印banner、配置spring.beaninfo.ignore系统属性等等,不细写,接下来就是创建ConfigurableApplicationContext对象,通过applicationContextFactory创建对应的上下文,本文是基于常规的web应用来做的,所以默认上下文是通过AnnotationConfigServletWebServerApplicationContext来创建的,另一个上下文类型是AnnotationConfigReactiveWebServerApplicationContext响应式web的上下文。上下文创建好了过后,就开始执行核心方法prepareContext:
- 设置上下文的Environment环境变量
- ApplicationContext前置处理,设置BEAN_NAME_GENERATOR、ResourceLoader、ClassLoader、类型转换器ConversionService
- 执行所有的ApplicationContextInitializer,这里有几个重要的上下文初始化器要注意:
- DelegatingApplicationContextInitializer,委派初始化器,可以执行context.initializer.classes配置的自定义初始化器
- ContextIdApplicationContextInitializer,设置上下文ID,取的是spring.application.name配置的值,如果没有配置,则默认为application
- ServerPortInfoApplicationContextInitializer,监听WebServerInitializedEvent事件,可以基于server.port配置来设置服务监听端口号
- 触发监听者contextPrepared事件
- 广播BootstrapContextClosedEvent事件,关闭引导上下文对象
- beanFactory注册applicationArguments、printedBanner对象,另外设置allowCircularReferences是否允许循环引用、allowBeanDefinitionOverriding是否允许bean定义被覆盖
- 如果设置了懒初始化上下文,那么添加LazyInitializationBeanFactoryPostProcessor对象到bean工厂前置处理器中
- 加载所有资源类,默认就是Application启动类,并且注册Application类到上下文中
- 触发监听者的contextLoaded事件
5、执行refreshContext方法刷新上下文
这个也是run方法中的核心逻辑,刷新上下文,这里执行了很多方法和逻辑,下面一一列举:
- 给上下文添加一个shutdownHook
- 预处理上下文信息:
- 记录服务启动是时间戳
- 设置当前服务 context 关闭标记(closed),活动标记(active)
- 根据需要执行特殊的属性初始化操作。
- 校验必须配置属性是否设置成功
- 设置早期的事件监听列表 ApplicationListener 列表。
- 对获取的 BeanFactory 实例对象进行预处理:
- 设置 Bean 的类加载器(beanClassLoader)。
- 根据配置 spring.spel.ignore 添加表达式解析工具类实例(StandardBeanExpressionResolver)。
- 添加属性编辑器注册器(ResourceEditorRegistrar)。
- 添加 Aware 接口执行器(ApplicationContextAwareProcessor),并将 Aware的子接口 接口设置为忽略依赖接口。
- 注册 指定类型类的实例。BeanFactory.class, beanFactory
- 添加实现BeanPostProcessor接口的 Listener 装饰器 (ApplicationListenerDetector)。后续将实现 ApplicationListener 的bena添加到 applicationListeners 列表钟。
- 如果配置了loadTimeWeaver实现,则添加实现BeanPostProcessor接口的 LoadTimeWeaverAwareProcessor。后续给实现LoadTimeWeaverAware 的bean设置LoadTimeWeaver实现对象。实际中使用较少。
- 将环境变量和系统属性相关的对象实例注册到 单例Bean Map中。
getEnvironment().getSystemProperties() JVM 启动时相关的环境属性信息
getEnvironment().getSystemEnvironment() OS 系统的环境变量相关属性信
- invokeBeanFactoryPostProcessors(beanFactory)
回调 BeanFactoryPostProcessors 实现类的postProcessBeanFactory方法,进行BeanFactory后置处理。
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
- 根据 PriorityOrdered,Ordered以及常规的 BeanDefinitionRegistryPostProcessor 实现类 顺序依次调用 的 postProcessBeanDefinitionRegistry 方法。
- 根据 PriorityOrdered,Ordered以及常规的 BeanFactoryPostProcessors 实现类 顺序依次调用 的postProcessBeanFactory方法。
- registerBeanPostProcessors(beanFactory)
向 BeanFactory 中注册 BeanPostProcessor 实现类。
PostProcessorRegistrationDelegate.registerBeanPostProcessors
1.从 BeanFactory 取出 beanDefinitionMap 中所有 BeanPostProcessor 实现类
2. 按 PriorityOrdered,Ordered以及nonOrdered 顺序添加到 BeanFactory的beanPostProcessors 列表中。
如果 一个实现类继承 MergedBeanDefinitionPostProcessor 接口时,则添加到列表最后。
- registerListeners()
注册 Listeners (列表),广播 earlyApplicationEvents 事件。
1.将 context 的 applicationListeners (列表) 添加到广播器(applicationEventMulticaster)的applicationListeners(列表)中。
2.将 BeanFactory 中管理的所有 ApplicationListener 类型Bean添加到广播器(applicationEventMulticaster) applicationListenerBeans(列表)中。
3.将 earlyApplicationEvents 事件列表发送到广播器(applicationEventMulticaster)。将遍历触发满足监听事件的 ApplicationListener.onApplicationEvent 方法。 - finishBeanFactoryInitialization(beanFactory)
BeanFactory 最终的初始化操作- beanFactory 设置 ConversionService 实现Bean。
- beanFactory 设置 EmbeddedValueResolver 实现Bean。
- 获取 beanFactory 所有 LoadTimeWeaverAware 类型 bean。并创建 bean 实例。
- beanFactory 设置 tempClassLoader 为 null。阻止再使用 tempClassLoader
- beanFactory 冻结配置 beanDefinitionNames。为后面 批量处理单例 Bean 做准备。
- beanFactory 获取所有非懒加载的单例 bean对象。
- finishRefresh()
上下文(context) 后续处理逻辑。
- 清理资源缓存(resourceCaches)信息
- 设置 context 的生命周期处理器 (LifecycleProcessor)。如果没有配置,则使用 DefaultLifecycleProcessor 实现。
- 调用 LifecycleProcessor.onRefresh 方法。获取 beanFactory 中所有 Lifecycle 类型bean,按 Phased 分组进行排序,然后遍历执行 Lifecycle.start 方法。
- 发布 ContextRefreshedEvent 事件。先向当前上下文(context) 环境发送事件广播。然后向父类发布 ContextRefreshedEvent 事件广播。
- 获取 平台 MBeanServer 实现,将 LiveBeansView 和 applicationName 注册到 MBeanServer 中。
- resetCommonCaches()
清理公共缓存。
1.清理反射中缓存的方法和字段信息。
2.清理注解缓存的信息。
3.清理解析类型相关的缓存信息。
4.清理类加载器相关的信息。
6、收尾逻辑
继续执行run方法剩余逻辑
- 触发监听者的started方法
- 执行callRunners
- 先执行ApplicationRunner
- 再执行CommandLineRunner
- 触发监听者的ready(running)方法
以上就是整个springboot的启动逻辑,整个过程还是很复杂,而且很灵活,用了很多函数式编程,看完代码过后也会受到熏陶,记录观摩心得,以便后续回顾。
标题:springboot(2.4.1)源代码分析六springboot启动逻辑总结
作者:michael
地址:https://blog.junxworks.cn/articles/2022/12/27/1672105698192.html