本文代码基于 Springboot 2.1.0
概述
在包org.springframework.context
中提供了这样一个接口ApplicationContextInitializer
:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**
* 初始化给定的 application context.
* @param applicationContext 需要被初始化的 application context
*/
void initialize(C applicationContext);
}
这是一个用来初始化Spring ConfigurableApplicationContext
应用上下文的回调接口,设定的调用时机是在ConfigurableApplicationContext#refresh()
调用之前。
该接口典型的应用场景是web应用中需要编程方式对应用上下文做初始化。比如,注册属性源(property sources
)或者针对上下文的环境信息environment
激活相应的profile
。
使用分析
在一个Springboot应用中,classpath上会包含很多jar包,有些jar包需要在ConfigurableApplicationContext#refresh()
调用之前对应用上下文做一些初始化动作,因此它们会提供自己的ApplicationContextInitializer
实现类,然后放在自己的META-INF/spring.factories
属性文件中,这样相应的ApplicationContextInitializer
实现类就会被SpringApplication#initialize
发现:
// SpringApplication#initialize方法,在其构造函数内执行,从而确保在其run方法之前完成
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));// <===================
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
然后在应用上下文创建之后,应用上下文刷新(refresh
)之前的准备阶段被调用 :
// SpringApplication#run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner); // <===================
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
// SpringApplication#prepareContext方法
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context); // <==============
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
}
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);
}
}
常见的一些ApplicationContextInitializer
实现
下面列出了一个使用缺省配置的springboot web应用所使用到的ApplicationContextInitializer
实现:
实现类 | 包 | 介绍 |
---|---|---|
DelegatingApplicationContextInitializer | org.sf.boot.context.config | 使用环境属性context.initializer.classes 指定的初始化器(initializers )进行初始化工作,如果没有指定则什么都不做 |
ContextIdApplicationContextInitializer | org.sf.boot.context | 设置Spring应用上下文的ID,会参照环境属性:spring.application.name vcap.application.name spring.config.name spring.application.index vcap.application.instance_index PORT 如果这些属性都没有,ID使用"application"。 |
ConfigurationWarningsApplicationContextInitializer | org.sf.boot.context | 对于一般配置错误在日志中作出警告 |
ServerPortInfoApplicationContextInitializer | org.sf.boot.context.embedded | 将内置servlet容器实际使用的监听端口写入到Environment 环境属性中。这样属性"local.server.port"就可以直接通过@Value 注入到测试中,或者通过环境属性Environment 获取。 |
SharedMetadataReaderFactoryContextInitializer | org.sf.boot.autoconfigure | 创建一个SpringBoot和ConfigurationClassPostProcessor 共用的CachingMetadataReaderFactory 对象,实现类使用ConcurrentReferenceCachingMetadataReaderFactory |
ConditionEvaluationReportLoggingListener | org.sf.boot.autoconfigure.logging | 将ConditionEvaluationReport 写入日志 |
注意 : 上表中包名中的
springframework
缩写为sf