本文采用知识共享 署名-相同方式共享 4.0 国际 许可协议进行许可。
访问 https://creativecommons.org/licenses/by-sa/4.0/ 查看该许可协议。
简介
ApplicationContextInitializer
是啥呢,我们来看一下源码注释中的介绍:
* Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
* 它是 Spring 初始化过程中的一个回调接口,会在 ConfigurableApplicationContext 的 refresh 方法执行前调用
所以我们可以在 SpringBoot 的这个回调点,去对 ApplicationContext
做一些事情 =。=
我们有三种方法让 SpringBoot 去加载它的实现:
0) 定义初始化器
我们可以实现 ApplicationContextInitalizer
来定义我们的初始化器,先来看一哈这个接口源码:
/**
* Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
*
* <p>Typically used within web applications that require some programmatic initialization
* of the application context. For example, registering property sources or activating
* profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()
* context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support
* for declaring a "contextInitializerClasses" context-param and init-param, respectively.
*
* <p>{@code ApplicationContextInitializer} processors are encouraged to detect
* whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been
* implemented or if the @{@link org.springframework.core.annotation.Order Order}
* annotation is present and to sort instances accordingly if so prior to invocation.
*
* @author Chris Beams
* @since 3.1
* @param <C> the application context type
* @see org.springframework.web.context.ContextLoader#customizeContext
* @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM
* @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses
* @see org.springframework.web.servlet.FrameworkServlet#applyInitializers
*/
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}
实现它需要一个泛型,可以传入 ConfigurableApplicationContext
的实现,我们可以根据需要选择对应的实现,一般直接用它本身即可。
且可以使用 @Order
调整加载的顺序,这里仅演示注入一个 Environment
:
@Order(1)
public class InjectInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
Map<String, Object> propertyMap = Map.of("InjectKey", "InjectValue");
MapPropertySource propertySource = new MapPropertySource("InjectProperty", propertyMap);
applicationContext.getEnvironment().getPropertySources().addLast(propertySource);
}
}
1) 定义 /META-INF/spring.factories 注入
如果翻阅 SpringBoot 源码会得知启动过程中会从 /META-INF/spring.factories
中取得加载器并执行
,spring-boot.jar
和 spring-boot-autoconfigure.jar
中有此配置文件例子,我们可以参考它们的格式编写我们的 spring.factories
,放进 classpath 下的 META-INF 中:
# Application Context Initializers, 需添加多个可用 ',' 分隔
org.springframework.context.ApplicationContextInitializer=\
cat.wars.inject.InjectInitializer,\
cat.wars.inject.Inject2Initializer
2) 通过 SpringApplication 注入
通常我们的 SpringBoot 启动主函数是这样写的:
SpringApplication.run(Application.class, args);
可以拆分为两行:
SpringApplication application = new SpringApplication(Application.class);
application.run(args);
可以使用 SpringApplication.addInitializers
方法添加我们的初始化器
SpringApplication application = new SpringApplication(Application.class);
application.addInitializers(new InjectInitializer());
application.run(args);
3) 配置文件注入
spring-boot.jar 中的 spring.factories
中定义加载了 DelegatingApplicationContextInitializer
,其中会读取配置文件中 context.initializer.classes
属性,所以我们还能通过定义配置文件注入。
context:
initializer:
classes: cat.wars.course.multidb.initial.InjectInitializer,cat.wars.course.multidb.initial.Inject2Initializer
4) 总结
SpringBoot 中至少有三种方法,可以注入我们定义的 Spring 上下文初始化器,分别是:
- 定义
/META-INF/spring.factories
注入 - 通过
SpringApplication
的addInitializers
方法注入 - 通过配置文件中
context.initializer.classes
属性注入