spring-boot自定义容器初始化组件

前言
前段时间我们在分享spring boot启动过程的时候,提到有几个dome要分享下,至于分享这些dome的原因主要有以下几点,第一点是结合spring boot启动过程从实践的角度看这些组件的用法和作用;第二点是,从源码的角度发掘spring boot潜在的高级知识点,学习和消化这些知识点,实现自我提升,所以从今天开始我们会逐一把前面说的demo分享出来,今天我们先来看下容器初始化组件——ApplicationContextInitializer。
自定义初始化组件
初始化组件
定义一个初始化组件,实现ApplicationContextInitializer,然后在initialize方法中写入我们要进行的初始化操作:
/**
 * 自定义容器初始化类
 *
 * @author syske
 * @version 1.0
 * @date 2021-09-15 8:02
 */
public class SyskeInitializer implements ApplicationContextInitializer {
    private Logger logger = LoggerFactory.getLogger(SyskeInitializer.class);
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        logger.info("====================start======================");
        logger.info("SyskeInitializer initialize方法被执行……");
        logger.info("ApplicationName: {}", configurableApplicationContext.getApplicationName());
        logger.info("isActive: {}", String.valueOf(configurableApplicationContext.isActive()));
        logger.info("====================end======================");
    }
}
这样我们的初始化组件就写好了。
配置
在resources文件夹下创建META-INF文件夹,并在其下创建sping.factories文件,具体结构如下:

然后在spring.factories文件中加入初始化组件的配置:
org.springframework.context.ApplicationContextInitializer=io.github.syske.springbootlearning.initializer.SyskeInitializer
这里的org.springframework.context.ApplicationContextInitializer就是我们前面实现的初始化接口,io.github.syske.springbootlearning.initializer.SyskeInitializer就是我们的初始化组件的具体实现,加这个配置的作用有两点,一个就是让spring boot能够识别我们自己增加的组件,二一个就是声明我们组件的类型是org.springframework.context.ApplicationContextInitializer,这样spring boot在启动的过程中就会按初始化组件进行处理。
这种配置方式通常被称作SPI机制(Service Provider Interface),在各类第三方框架中经常出现,比如dubbo、spring boot,通过这种方式既可以为框架提供各种扩展,同时又可以降低框架与扩展之间的依赖,可以实现有效解耦,确实很方便,当然也很强大。
在spring boot中除了org.springframework.context.ApplicationContextInitializer之外,还为我们提供了如下扩展:
- org.springframework.boot.env.PropertySourceLoader:- Property资源加载器
- org.springframework.boot.SpringApplicationRunListener:容器运行监听器
- org.springframework.boot.SpringBootExceptionReporter:异常报告器
- org.springframework.context.ApplicationListener:应用监听器
- org.springframework.boot.env.EnvironmentPostProcessor:环境变量后置处理器
- org.springframework.boot.diagnostics.FailureAnalyzer:失败分析器
- org.springframework.boot.diagnostics.FailureAnalysisReporter:失败分析报告器
当然,以上这些可能补充的也不是很完整,这里仅供参考。用法上和我们今天演示的ApplicationContextInitializer基本上都是一样的,各位小伙伴可以自行尝试。
另外需要提一点的是,spring-boot的starter也是基于spring.factories来实现的,感兴趣的小伙伴可以回顾下:
启动测试
完成以上代码编写和配置工作以后,我们直接运行启动spring boot,然后会在控制台输出我们在initialize方法输出的内容:

从上面的启动日志中,我们可以发现,ApplicationContextInitializer组件是紧挨着banner打印被执行的,结合我们最近分析的启动过程,我们可以知道,初始化组件是在prepareContext方法中被执行的,而这个方法是紧挨着容器创建的:

从setInitializer方法这里我们可以看到,我们自己定义的ApplicationContextInitalizer已经被注册到initializers中了

加载过程分析
具体的配置和用法分享完了,下面我们结合最近研究的spring boot启动过程分析下spring.factories的加载过程。
spring.factories文件需要放在META-INF文件夹下,注意观察文件名,我们可以发现,文件后缀刚好是factory的复数形式,这也说明了这个文件的用途。它是在run方法中被解析的,更准确的说是在实例化SpringApplication的时候被解析的:
首先我们在程序主入口中调用了SpringApplication的静态方法run:

然后静态run方法调用了另一个静态run,并在第二个静态run方法内部实例化SpringApplication:

实例化SpringApplication的时候,会同步调用setInitializers(紧挨着的setListeners是设置应用监听器的),这里设置的就是容器初始化组件(包括我们自定义的),这里获取初始化组件的方法getSpringFactoriesInstances其内部进行了spring.factories文件的解析操作:

下面就是getSpringFactoriesInstances方法的内部实现:
其中的SpringFactoriesLoader.loadFactoryNames就是解析并获取spring.factories文件中的Factory的名字,然后将最终结果放进缓存中并返回:


总结
今天我们分享了自定义初始化组件,演示了定义过程、配置方式以及最终测试结果,整体来说整个过程还是比较简单的,当然重要的还是如何将这一组件和我们的具体业务相结合,实现我们具体的业务,这应该才是我们应该思考的。好了,关于spring boot初始化组件我们暂时就分享这么多,有兴趣的小伙伴可以亲自动手试一下。
