目录
一、 ApplicationContextInitializer 介绍
二、三种实现方式
2.1、mian函数中添加
2.2、配置文件中配置
2.3、SpringBoot的SPI扩展---META-INF/spring.factories中配置
三、排序问题
四、通过源码分析ApplicationContextInitializer何时被调用
一、 ApplicationContextInitializer 介绍
ApplicationContextInitializer用于在spring容器刷新之前初始化Spring ConfigurableApplicationContext的回调接口。(剪短说就是在容器刷新之前调用该类的 initialize 方法。并将 ConfigurableApplicationContext 类的实例传递给该方法)
- 通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,根据上下文环境注册属性源或激活配置文件等。
- 可排序的(实现Ordered接口,或者添加@Order注解)
看完这段解释,为了讲解方便,我们先看自定义 ApplicationContextInitializer 的三种方式。再通过SpringBoot的源码,分析生效的时间以及实现的功能等。
二、三种实现方式
首先新建一个类 MyApplicationContextInitializer 并实现 ApplicationContextInitializer 接口。
@Slf4j
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
log.info("-----执行-----------MyApplicationContextInitializer initialize-----");
}
}
2.1、mian函数中添加
优雅的写一个SpringBoot的main方法
@SpringBootApplication
public class SpringBootSourceApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(SpringBootSourceApplication.class);
application.addInitializers(new MyApplicationContextInitializer());
application.run(args);
}
}
2.2、配置文件中配置
在application.properties配置
context.initializer.classes=com.alen.springbootsource.initializer.MyApplicationContextInitializer
对于这种方式是通过DelegatingApplicationContextInitializer这个初始化类中的initialize方法获取到application.properties中context.initializer.classes对应的类并执行对应的initialize方法。只需要将实现了ApplicationContextInitializer的类添加到application.properties即可。
private static final String PROPERTY_NAME = "context.initializer.classes";
private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
String classNames = env.getProperty(PROPERTY_NAME);
List<Class<?>> classes = new ArrayList<Class<?>>();
if (StringUtils.hasLength(classNames)) {
for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
classes.add(getInitializerClass(className));
}
}
return classes;
}
是从配置文件中获取到对应的初始化类信息,然后执行初始化方法。
2.3、SpringBoot的SPI扩展---META-INF/spring.factories中配置
org.springframework.context.ApplicationContextInitializer=com.alen.springbootsource.initializer.MyApplicationContextInitializer
这个加载过程是在SpringApplication中的getSpringFactoriesInstances()方法中直接加载并实例后执行对应的initialize方法。代码如下:
private <T> Collection<? extends 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<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
三、排序问题
如图所示改造一下mian方法。打一个断点,debug查看排序情况。
给 MyApplicationContextInitializer 加上Order注解:我们指定其拥有最高的排序级别。(越高越早执行)
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
log.info("-----执行-----------MyApplicationContextInitializer initialize-----");
}
}
下面我们通过debug分别验证二章节中提到的三种方法排序是否都是可以的。
首先验证2.1章节中采用的main函数中添加:debug,断点处查看 application.getInitializers() 这行代码的结果可见,排序生效了。
然后再分别验证2.2和2.3章节中的方法。排序都是可以实现的。
然而当采用2.3中的SPI扩展的方式,排序指定 @Order(Ordered.LOWEST_PRECEDENCE) 排序并没有生效。当然采用实现Ordered接口的方式,排序验证结果都是一样的。
四、通过源码分析ApplicationContextInitializer何时被调用
debug差看上文中自定的 MyApplicationContextInitializer 的调用栈。
可见 ApplicationContextInitializer 在容器刷新前的准备阶段被调用。 refreshContext(context); 在SpringBoot的启动函数中, ApplicationContextInitializer
public ConfigurableApplicationContext run(String... args) {
//记录程序运行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// ConfigurableApplicationContext Spring 的上下文
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//从META-INF/spring.factories中获取监听器
//1、获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//2、构造容器环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//处理需要忽略的Bean
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
///3、初始化容器
context = createApplicationContext();
//实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[]{ConfigurableApplicationContext.class}, context);
//4、刷新容器前的准备阶段
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//5、刷新容器
refreshContext(context);
//刷新容器后的扩展接口
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
然后看在 refreshContext(context); 具体是怎么被调用的。
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
...
}
然后在 applyInitializers 中遍历调用每一个被加载的 ApplicationContextInitializer 的 initialize(context); 方法,并将 ConfigurableApplicationContext 的实例传递给 initialize 方法。
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);
}
}
OK,到这里通过源码说明了 ApplicationContextInitializer 是何时及如何被调用的。