在【springboot高级】(一:自定义容器初始化器的三种方式,实现ApplicationContextInitializer接口。(使用篇))讲到了说通过配置文件中进行配置的优先级比较高,而且我们打的@Order注解对其没有效果。那么这节就通过源码的方式来揭开他的真面目。
关键字:DelegatingApplicationContextInitializer、order、context.initializer.classes、spring.factories
在讲之前,我们先熟悉一下DelegatingApplicationContextInitializer这个类,他也是一个初始化器。
如何确认:
从上图中我们可以看到,springboot下面的spring.factories文件中配置有DelegatingApplicationContextInitializer初始化类。所以他也是一个初始化器。
它位于org.springframework.boot.context.config包下面。
/**
* {@link ApplicationContextInitializer} that delegates to other initializers that are
* specified under a {@literal context.initializer.classes} environment property.
*
* @author Dave Syer
* @author Phillip Webb
* @since 1.0.0
*/
public class DelegatingApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
private int order = 0;
private static final String PROPERTY_NAME = "context.initializer.classes";
}
看它类上面的注释,大概意思是ApplicationContextInitializer委托给DelegatingApplicationContextInitializer来加载配置文件中的初始化类。
这样设计的目的没有懂,应该是有他自己的原因。他的作用就是获取到我们配置文件中配置的初始化器的权限定类名,然后执行一遍applyInitializerClasses方法。而他本身也是一个初始化器,所以我们在配置文件中配置的初始化器一定是紧随其后的。
然后我们看他继承了Ordered接口,然后order属性为0,这个优先级还是比较高的了,所以之前我们说的先执行,就是因为他的order为0,并且他的初始化方法就是获取配置文件中的自定义初始化器,然后执行一遍applyInitializerClasses方法
我们来看他的初始化方法:
@Override
public void initialize(ConfigurableApplicationContext context) {
// 首先获取上下文环境配置
ConfigurableEnvironment environment = context.getEnvironment();
// 通过上下文环境获取到我们自定义的初始化器
List<Class<?>> initializerClasses = getInitializerClasses(environment);
// 如果不为空,那么就执行applyInitializerClasses方法,这就和我们第一节讲得通过SpringFactoriesLoade加载后执行的的方法相同了
if (!initializerClasses.isEmpty()) {
applyInitializerClasses(context, initializerClasses);
}
}
// 然后我们看getInitializerClasses方法
private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
// 由于我们的所有配置文件上下文环境都已经读取完成,这里通过上下文环境获取property,
// PROPERTY_NAME这个常量就是上面它定义好的:context.initializer.classes
String classNames = env.getProperty(PROPERTY_NAME);
List<Class<?>> classes = new ArrayList<>();
// 这里就进行拆分,因为我们配置可以有多个,所以通过 , 来进行拆分成为一个数组
if (StringUtils.hasLength(classNames)) {
// 拆分循环
for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
// 获取到配置的Class对象,然后添加进classes集合中。
classes.add(getInitializerClass(className));
}
}
// 返回到上一步,然后去调用applyInitializerClasses方法。
return classes;
}
// 我们看一下他是如何获取Class对象的:getInitializerClass
private Class<?> getInitializerClass(String className) throws LinkageError {
try {
// 同样,通过反射获取,
Class<?> initializerClass = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
Assert.isAssignable(ApplicationContextInitializer.class, initializerClass);
return initializerClass;
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex);
}
}
从这上面分析下来,我们就可以得知,如果我们配置文件中配置的初始化器,那么他的@Order注解是不生效的,因为执行这里的时候,他没有经过排序那个方法,所以他们的@Order注解的值都是0。