前言
上文粗略的看了springboot启动相关的源码,这次我们来看SpringApplication构造方法中初始化的ApplicationContextInitializer的作用
ApplicationContextInitializer主要用于在spring创建上下文之前调用prepareContext的时候触发一系列操作主要是对上下文进行配置
实现了ApplicationContextInitializer接口的类都会在spring启动调用prepareContext的时候执行
ApplicationContextInitializer三种配置方式
1-项目配置文件配置
1-bootstrap.yml配置或者application.yml进行配置
如图
代码如下
/**
* 项目配置文件注入方式
*/
public class TestInit2 implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("TestInit2-----------项目配置文件注入方式");
}
}
/**
* application.yml项目配置文件注入方式
*/
public class TestInit2_1 implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("TestInit2-1-----------项目配置文件注入方式");
}
}
2-项目代码配置
也可以在项目中进行配置添加如下
@SpringBootApplication
public class TestServerApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(TestServerApplication.class);
springApplication.addInitializers(new TestInit0()); //添加ApplicationContextInitializer
ConfigurableApplicationContext context = springApplication.run(args);
context.close();
}
}
3-spring工厂配置文件配置
也可以在项目中通过spring.factories进行配置 主要是在项目的resources下添加如下文件
在spring.factories中进行如下配置
org.springframework.context.ApplicationContextInitializer=com.tuhuanjk.cloud.config.access.TestInit1
执行结果输出如下
···
···
执行结果解析
执行顺序
可以在代码上使用@Order注解进行配置执行顺序 @Order的value值越小越早执行
ApplicationContextInitializer三种配置执行的默认先后顺序及源码位置
由执行结构可知在spring.factories中配置的类会加载并执行两次
同时执行默认顺序为
1 bootstrap中配置
2 spring.factories中配置
3 aoolication中配置
4 spring.factories中配置
5 代码中配置
这是因为spring boot会在创建application上下文时通知BootstrapApplicationListener并在BootstrapApplicationListener中通过SpringApplicationBuilder创建一次bootstrap的ConfigurableApplicationContext上下文并调用run方法 这里就会输出 1 和 2
然后再接着创建application上下文 这里就会输出3,4,5
具体原理可以参考我的另一篇文章
https://www.jianshu.com/p/f038d9e12554
1-在spring.factories中配置的类最先加载
通过SpringApplication构造方法中的如下代码进行读取配置文件并加载入SpringApplication的initializers集合(ArrayList)
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
2-在boostrap中配置的加载顺序为第二
在创建bootstrap上下文时先通过ConfigFileApplicationListener将配置读入内存然后再通过DelegatingApplicationContextInitializer进行加载配置的类并调用initialize方法进行执行关键代码在DelegatingApplicationContextInitializer的initialize方法中如下代码
DelegatingApplicationContextInitializer的方法
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
List<Class<?>> initializerClasses = this.getInitializerClasses(environment);
if (!initializerClasses.isEmpty()) {
this.applyInitializerClasses(context, initializerClasses);
}
}
获取在bootstrap中配置的ApplicationContextInitializer类
private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
String classNames = env.getProperty("context.initializer.classes");
List<Class<?>> classes = new ArrayList();
if (StringUtils.hasLength(classNames)) {
String[] var4 = StringUtils.tokenizeToStringArray(classNames, ",");
int var5 = var4.length;
for(int var6 = 0; var6 < var5; ++var6) {
String className = var4[var6];
classes.add(this.getInitializerClass(className));
}
}
return classes;
}
执行在bootstrap中配置的ApplicationContextInitializer类
private void applyInitializerClasses(ConfigurableApplicationContext context, List<Class<?>> initializerClasses) {
Class<?> contextClass = context.getClass();
List<ApplicationContextInitializer<?>> initializers = new ArrayList();
Iterator var5 = initializerClasses.iterator();
while(var5.hasNext()) {
Class<?> initializerClass = (Class)var5.next();
//将配置的类实例化并放入spring容器
initializers.add(this.instantiateInitializer(contextClass, initializerClass));
}
//回调对应的initialize方法
this.applyInitializers(context, initializers);
}
3-application.yml中配置的加载顺序为第三
加载代码和在bootstrap.yml中配置 加载方式一样,只是因为bootstrap.yml中配置的 在创建的bootstrap上下时候就执行了而在application.yml中配置的在创建application上下文时候才执行 所以有先后顺序
4-代码中配置的加载顺序为第四
通过代码中显示的配置直接将类加载入SpringApplication的initializers集合 如下代码
springApplication.addInitializers(new TestInit0());