ApplicationContextInitializer使用以及加载的原理
一、ApplicationContextInitializer接口用于ConfigurableApplicationContext通过调用refresh函数来初始化Spring容器之前的回调函数;通常在web应用中,设计在初始化Spring容器之前调用。例如依赖于容器ConfigurableApplicationContext中的Enviroment来记录一些配置信息或者使一些配置文件生效...
一、ApplicationContextInitializer接口
用于ConfigurableApplicationContext通过调用refresh函数来初始化Spring容器之前的回调函数;
通常在web应用中,设计在初始化Spring容器之前调用。例如依赖于容器ConfigurableApplicationContext中的Enviroment来记录一些配置信息或者使一些配置文件生效;
参考ContextLoader和FrameworkServlet中支持定义contextInitializerClasses作为context-param或定义init-param。
支持Order注解,表示执行顺序,越小越早执行;
二、用法
定义一个ApplicationContextInitializer实现类。
public class DemoApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
System.err.println("-----------------------------------------------" + this.getClass().getSimpleName());
}
}
public class DemoApplicationContextInitializer2 implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
System.err.println("-----------------------------------------------" + this.getClass().getSimpleName());
}
}
1.手动添加
2.在项目resources目录下新建META-INF/spring.factories文件,在配置文件加入如下配置项org.springframework.context.ApplicationContextInitializer = com.springboot.helloworld.applicationInitalizer.SpringFactoryApplicationContextInitializer
参考spring-boot-2.1.6.RELEASE.jar中的配置;
3.在项目resources目录下新建application.properties文件,在配置文件中加入如下配置项(如果有多个,用,隔开)context.initializer.classes = com.springboot.helloworld.applicationInitalizer.ApplicationPropertiesApplicationContextInitializer
三、ApplicationContextInitializer分析
1.在手动添加的用法中,自定义的ApplicationContextInitalizer实例会存储到SpringApplication实例的initializers集合中,该集合的调用是如何触发的呢?
在SpringApplication类中的run方法,有如下相关代码:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); #刷新容器前,初始化
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
prepareContext方法的核心代码如下:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
this.applyInitializers(context); #遍历initializers集合中的SpringApplicationInitializer实例
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
SpringApplication中的applyInitializers方法就是遍历initializers集合中的SpringApplicationInitializer实例,调用其initialize方法。
protected void applyInitializers(ConfigurableApplicationContext context) {
Iterator var2 = this.getInitializers().iterator();
while(var2.hasNext()) {
ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
综上,ApplicationContextInitializer的调用时机,是在容器刷新之前的prepareContext方法,通过获取ApplicationContextInitializer的集合,遍历调用initialize方法。
2.实例化SpringApplication对象使,其构造方法如下(对应用法2):
public SpringApplication(Class... primarySources) {
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
其中会调用setInitializers方法设置SpringApplicationInitializer集合;
getSpringFactoriesInstances(ApplicationContextInitializer.class)逻辑如下:
(1)该方法会扫描所有的META-INF/spring.factorties文件
(2)获取key为org.springframework.context.ApplicationContextInitializer的所有属性值(本质上是ApplicationContextInitializer的实现类的类全名)
(3)然后使用其对应类的无参构造器进行反射实例化并进行排序,然后设置到SpringApplication实例中的initializers集合中;
所以我们创建spring.factories文件中的自定义ApplicationContextInitializer会加入到initializers集合中。
spring-boot-2.1.6.RELEASE.jar中的spring.factories文件定义的ApplicationContextInitializer实现如下:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
3.分析上面第2步中所说的spring-boot中自带的DelegatingApplicationContextInitializer类的initialize方法(对应用法3)
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
List<Class<?>> initializerClasses = this.getInitializerClasses(environment);
if (!initializerClasses.isEmpty()) {
this.applyInitializerClasses(context, initializerClasses);
}
}
从容器上下文中的Environment中获取配置名为context.initializer.classes的属性值(多个用,隔开,实际配置的是类全名),如果存在就通过反射实例化这些ApplicationContextInitialzier对象并排序,遍历并调用其initialize方法。
该类实际上是一个委托类,将实际的初始化工作交给了context.initializer.classes环境变量指定的ApplicationContextInitialize对象;
四、ApplicationContextInitializer执行顺序
springboot中自带的DelegatingApplicationContextInitializer类的排序值为0,是springboot自带的ApplicationContextInitializer中排序最小,最先执行的类。(如果ApplicationContextInitializer没有实现Orderd接口,那么其排序值默认是最大,最后执行)
所以可以得到其执行顺序如下
1.如果我们通过DelegatingApplicationContextInitializer委托来执行我们自定义的ApplicationContextInitializer,那么我们自定义的ApplicationContextInitializer的顺序一定是在系统自带的其他ApplicationContextInitializer之前执行。
2.如果我们通过SpringApplication实例对象调用addInitializers方法加入自定义的ApplicationContextInitializer,那么spring-boot自带的ApplicationContextInitializer会先按顺序执行,再执行我们手动添加的自定义ApplicationContextInitializer(按照添加顺序执行),最后执行spring-boot自带的其他ApplicationContextInializer
3.如果我们创建自己的spring.factories文件,添加配置加入我们自定义的ApplicationContextInitializer,那么我们自定义的ApplicationContextInitializer会和spring-boot自带的ApplicationContextInitializer放在一起进行排序执行。
实验结果如下:
更多推荐
所有评论(0)