SpringBoot 框架原理,你不知道的事件回调机制
几个重要的事件回调机制
ApplicationContextInitializer
ApplicationContextInitializer 来源于 Spring 框架
主要作用就是在 ConfigurableApplicationContext 类型或者子类型的 ApplicationContext 做 refresh 之前
允许对 ConfigurableApplicationContext 的实例做进一步的设置和处理
ApplicationContextInitializer 接口:
是在 Spring 容器刷新之前执行的一个回调函数
是在 ConfigurableApplicationContext 的 refresh() 方法之前,即在 Spring 框架内部执行 ConfigurableApplicationContext 的 refresh() 方法或者 SpringBoot 的 run() 方法之前调用
作用是初始化 Spring 的 ConfigurableApplicationContext 的回调接口
通常用于需要对应用上下文进行初始化的 web 应用程序中: 比如根据上下文环境注册属性或者激活概要文件
使用分析
ApplicationContextInitializer 接口的典型应用场景:
对 web 应用程序的应用上下文进行初始化
比如:
注册属性源 property sources
针对上下文的环境信息 environment 激活相应的 profile
在一个 SpringBoot 的应用程序中:
classpath 上有很多 jar 包,有些 jar 包需要在 ConfigurableApplicationContext 的 refresh() 方法调用之前对应用上下文做一些初始化动作
因此会提供自己的 ApplicationContextInitializer 实现类,然后配置在自己的 META-INF/spring.factories 属性文件中
这样相应的 ApplicationContextInitializer 实现类就会被 SpringApplication 的 initialize() 方法发现
SpringApplication 的 initialize() 方法,在 SpringApplication 的构造函数内执行,从而确保在 SpringApplication 的 run() 方法之前完成
然后在应用上下文创建之后,应用上下文刷新之前的准备阶段被调用
SpringBoot 内置的 ApplicationContextInitializer
使用 SpringBoot web 应用默认使用的 ApplicationContextInitializer 的实现:
DelegatingApplicationContextInitializer:
使用环境属性 context.initializer.classes 指定的初始化容器 initializer 进行初始化工作,如果没有指定则不进行任何操作
使得可以在 application.properties 中可以自定义实现类配置
ContextIdApplicationContextInitializer:
参照环境属性,设置 Spring 应用上下文的 ID
ID 值的设置会参照环境属性:
spring.application.name
vcap.application.name
spring.config.name
spring.application.index
vcap.application.instance_index
如果这些属性都没有 ,ID 使用 application
ConfigurationWarningApplicationContextInitializer:
对于一般配置错误在日志中做出警告
ServerPortInfoApplicationContextInitializer:
将内置 servlet 容器实际使用的监听端口写入到 environment 环境属性中
这样属性 local.server.port 就可以直接通过 @Value 注入到测试中或者通过环境属性 environment 获取
SharedMetadataReaderFactoryContextInitializer:
创建一个 SpringBoot 和 ConfigurationClassPostProcessor 共用的 CachingMetadataReaderFactory 对象
实现类为 ConcurrentReferrenceCachingMetadataReaderFactory
ConditionEvaluationReportLoggingListener:
将 ConditionEvaluationReport 写入日志
ApplicationContextInitializer 是 Spring 中允许在上下文刷新之前做自定义操作,如果需要对 Spring 的上下文进行深度整合,可以借助 ApplicationContextInitializer 进行很好的实现
spring-test 包里有一个注解 org.springframework.test.context.ContextConfiguration 中有一个属性可以指定 ApplicationContextInitializer 辅助集成测试时自定义对上下文进行预处理
扩展实现方式
编程方式
先定义 ApplicationContextInitializer:
在启动类中手动增加 initializer:
添加配置方式
添加配置的方式是通过 DelegatingApplicationContextInitializer 初始化类中的 initialize() 方法获取到 application.properties 中 context.initializer.class 对应的类并执行对应的 initialize() 方法
只需要将实现了 ApplicationContextInitializer 的类添加到 application.properties 即可
先定义一个实现了 ApplocationContextInitializer 的类
然后在 application.properties 中定义:
spring.factories 方式
SpringApplicationRunListener
ApplicationContextInitializer,SpringApplicationRunListener 需要配置在 META-INF/spring.factories 中
ApplicationRunner
CommandLineRunner
ApplicationRunner,CommandLineRunner 需要放在 IOC 容器中
启动流程
创建 SpringApplication 对象
调用 initialize(sources)方法创造对象
保存主配置类
判断当前是否为一个 web 应用
从类路径下找到 META-INF/spring.factories 配置的所有 ApplicationContextInitializer,然后保存起来
从从类路径下找到 META-INF/spring.factories 配置的所有 ApplicatListener
从多个配置类中找到有 main 方法的主配置类
运行 run 方法
获取 SpringApplicationRunListeners,从类路径下 META-INF/spring.factories
回调所有的获取 SpringApplicationRunListener.starting()方法
封装命令行参数
准备环境 prepareEnvironment,创建环境完成后回调 SpringApplicationRunListeners.environmentPrepared():表示环境准备完成
创建 ApplicationContext:决定创建 web 的 IOC 还是普通的 IOC
准备上下文环境 prepareContext:将 environment 保存到 IOC 中,并且调用 applyInitializers():回调之前保存的所有的 ApplicationContextInitializer 的 initialize 方法.然后回调 SpringApplicationRunListener 的 contextPrepared 方法
prepareContext 运行完成以后回调所有的 SpringApplicationRunListeners 的 contextLoaded()方法
刷新容器 refreshContext,IOC 容器初始化.在 web 应用中还会创建嵌入式的 tomcat.在 refreshContext,是扫描,创建.加载所有组件的地方(配置类,组件,自动配置)
调用 callRunner()从 IOC 容器中获取所有的 ApplicationRunner 和 CommandLineRunner.先回调 ApplicationRunner,后回调 CommandLineRunner
最后回调 SpringApplicationRunListeners 的 listeners.running(context)
整个 SpringBoot 应用启动完成以后返回启动的 IOC 容器
事件监听机制
ApplicationContextInitializer
SpringApplicationRunListener
ApplicationContextInitializer,SpringApplicationRunListener 需要配置在 META-INF/spring.factories 中
ApplicationRunner
CommandLineRunner
ApplicationRunner,CommandLineRunner 需要放在 IOC 容器中-==@Component==
SpringBoot 自定义 starter
starter:
这个场景需要的依赖是什么?
如何编写自动配置?
配置自动装配 Bean:将标注 @Configuration 的自动配置类,放在 classpath 下的==META-INF/spring.factories==文件中才能加载
模式:
启动器: 启动器是一个空 jar 文件,==仅提供辅助性依赖管理,依赖导入==,这些依赖用于自动装配或者其它类库.
总结
官方文档
源码
版权声明: 本文为 InfoQ 作者【攻城狮Chova】的原创文章。
原文链接:【http://xie.infoq.cn/article/61243107e7ea024052cbf586d】。文章转载请联系作者。
评论