一. SpingBoot 初始化器简介
- ApplicationContextInitializer实际上是Spring容器初始化前ConfigurableApplicationContext的回调接口,可以对上下文环境作一些操作,如运行环境属性注册、激活配置文件等。
- 通常自定义的ApplicationContextInitializer有三种注册方式,按照优先级如下:
配置文件 > 工厂方式 > 启动注册 (相同的注册方式中,可以使用@Order注解来指定优先级,值越小优先级越高)。
二. 自定义初始化器
自定义初始化器需要实现ApplicationContextInitializer接口。
java
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import java.util.HashMap;
import java.util.Map;
public class FirstInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map<String, Object> map = new HashMap<>();
map.put("key1", "value1");
MapPropertySource mapPropertySource = new MapPropertySource("firstInitializer", map);
environment.getPropertySources().addLast(mapPropertySource);
System.out.println("run FirstInitializer");
}
}
配置完之后,可以通过实现ApplicationContextAware接口去获取对应的值。
java
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
@Service
public class TestService implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public String test() {
return applicationContext.getEnvironment().getProperty("key1");
}
}
1. 通过META-INF配置
在resources目录新建/META-INFI/spring.factories文件,并添加如下配置:
shell
org.springframework.context.ApplicationContextInitializer=org.example.initializer.FirstInitializer
2. 通过addInitializers方法添加
java
public class Main {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(Main.class);
springApplication.addInitializers(new FirstInitializer());
springApplication.run(args);
}
}
3. 通过application.yaml配置
bash
context:
initializer:
classes: org.example.initializer.FirstInitializer
三. 源码分析
1. 初始化器注册
-
工厂加载方式
ApplicationContextInitializer工厂加载的实现在SpringApplication类的构造函数中,Spring容器初始化前,先通过getSpringFactoriesInstances得到所有实现类的集合,然后通过setInitializers注入到SpringApplication类的initializers属性中;
javapublic 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 = WebApplicationType.deduceFromClasspath(); this.bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); // 设置初始化器 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); } -
addInitializers方式
addInitializers方法实现逻辑比较简单,就是将初始化器直接添加到SpringApplication的成员变量initializers中。
javapublic void addInitializers(ApplicationContextInitializer<?>... initializers) { this.initializers.addAll(Arrays.asList(initializers)); } -
配置文件方式
当初始化器配置在文件application.yaml时,它的注册是通DelegatingApplicationContextInitializer来实现,DelegatingApplicationContextInitializer是SpringBoot的一个内置初始化器,他的优先级最高,所以通过配置文件设置初始化器具有最高的优先级。
主要的注册逻辑在DelegatingApplicationContextInitializer的initialize中实现。
javapublic void initialize(ConfigurableApplicationContext context) { ConfigurableEnvironment environment = context.getEnvironment(); // 获取配置文件中的初始化器的Class对象 List<Class<?>> initializerClasses = getInitializerClasses(environment); if (!initializerClasses.isEmpty()) { // 初始化配置的初始化器 applyInitializerClasses(context, initializerClasses); } }
2. 初始化执行时机
ApplicationContextInitializer接口实现类的执行时机是在SpringApplication的prepareContext方法,即Spring容器正式刷新前,准备上下文环境时,它里面的applyInitializers方法会遍历已经注册的初始化器,并调用initialize方法。
java
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
...
// 执行初始化器
applyInitializers(context);
...
}
java
protected void applyInitializers(ConfigurableApplicationContext context) {
// 遍历已经注册的初始化器,执行initialize方法
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}