
ApplicationContextInitializer
Spring 容器在刷新之前会初始化 ConfigurableApplicationContext
的回调接口,简单来说,就是在调用 [[refreshContext]] 刷新容器之前会执行此类的 initialize
方法。通过该拓展点,用户可以在整个spring容器还没被初始化之前做一些事情。
1、拓展分析
1.1 执行流程
如上图所示,主要入口为 applyInitializers
方法,该方法唯一作用是直接将已经注册的 Initializers 对象进行调用,完成相应的初始化操作。
// 3. 在这里将所有已经注册进来的 ApplicationContextInitializer 进行调用
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
1.2 注册流程
上述提到有一个注册 ApplicationContextInitializer
实现的地方,该注册方法在 SpringApplication
初始化的时候被调用。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = deduceWebApplicationType();
// 注册初始化对象位置
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
重点关注的就是 getSpringFactoriesInstances
方法,从命名中不难猜出来该方法是获取目标对象,并返回一个实现类的数组,完成注册。逐层追踪下去,可以发现最终在一个 getSpringFactoriesInstances
方法中进行实例化对象。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
整体流程就是 获取类加载器 --> 获取拓展 spring.factories
类名(这里使用了 Java 的 [[SPI]] 机制) --> 加载具体对象,最终将所有需要执行注册的 ApplicationContextInitializer
返回。
2、拓展使用
针对于该拓展可以想到的场景可能为,在最开始激活一些配置,或者利用这时候 class 还没被类加载器加载的时机,进行动态字节码注入等操作。
扩展方式为:
public class TestApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {}
}
因为这时候 spring 容器还没被初始化,所以想要自己的扩展的生效,有以下三种方式:
在启动类中用
springApplication.addInitializers(new TestApplicationContextInitializer())
语句加入;配置文件配置
context.initializer.classes=com.example.demo.TestApplicationContextInitializer
;Spring SPI 扩展,在 spring.factories 中加
org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializer
。
- 感谢你赐予我前进的力量