扩展点
ApplicationContextInitializer 接口用于在 Spring 容器【刷新】之前执行的一个回调函数,通常用于向 SpringBoot 容器中注入属性。
刷新指的是 org.springframework.context.ConfigurableApplicationContext#refresh,这是Spring容器启动的核心,在早期Spring版本(SpringBoot之前)就已经存在
我们打开prepareContext方法可以看到,有一个applyInitializers方法
applyInitializers从名称上可以看出,它是应用initializer的一个逻辑,可以看看具体的方法实现,就是遍历initializer集合,然后调用接口的initialize()
好了,我们现在知道了它的调用时机,但是集合中的initializer是怎么加载进去的呢?
加载方式
ApplicationContextInitializer有三种方式加载。
1,在 resources/META-INF/spring.factories 中配置
2,在启动类里调用SpringApplication类的addInitializers()方法
3,配置context.initializer.classes
方式一:
方式二:
@SpringBootApplication
public class HelloApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(HelloApplication.class);
springApplication.addInitializers(new MyApplicationInitializer());
springApplication.run(args);
}
}
方式三:
application.properties配置项,多个用逗号分隔
加载顺序
我们通过方式二和方式三配置多个自定义的initializer,启动可以看到SpringApplication中有多个Initializer
最后一个是MyApplicationInitializer,但是没有看到方式三配置的2个。
我们先来看看,为什么会有8个Initializer。因为SpringBoot框架本身定义了多个。
从spring-boot-autoconfigure.jar和spring-boot.jar中的spring.factories可以看到
也就是说SpringBoot框架本身用了方式二添加Initializer。
那方式三的2个Initializer去哪里了呢?其实方式三是一种比较隐藏的定义Initializer的方式,它是靠DelegatingApplicationContextInitializer来加载的。
从名称上可以看出来,这个Initializer是一个Delegating,它是一个类似代理的作用。
查看它的initialize()方法可以看到,它就是读取context.initializer.classes配置并执行对应的Initializer。
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
List<Class<?>> initializerClasses = this.getInitializerClasses(environment);
if (!initializerClasses.isEmpty()) {
this.applyInitializerClasses(context, initializerClasses);
}
}
private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
String classNames = env.getProperty("context.initializer.classes");
List<Class<?>> classes = new ArrayList();
if (StringUtils.hasLength(classNames)) {
String[] var4 = StringUtils.tokenizeToStringArray(classNames, ",");
int var5 = var4.length;
for(int var6 = 0; var6 < var5; ++var6) {
String className = var4[var6];
classes.add(this.getInitializerClass(className));
}
}
return classes;
}
从上面的解释可以分析出,大致执行顺序是 DelegatingApplicationContextInitializer -> SpringBoot官方其他的Initializer -> 项目本身spring.factories配置的Initializer->Application启动类中主动添加的Initializer
其实,除了DelegatingApplicationContextInitializer(order=0) ,后面的顺序可以自定义,使用Spring的Order注解。从源码可以看到对initializers集合是做了排序的。
参考
https://www.jianshu.com/p/6af81951fb4e
https://zhuanlan.zhihu.com/p/650183190