【springboot高级】(一:自定义容器初始化器的三种方式,实现ApplicationContextInitializer接口之在配置文件中配置。(源码篇))

【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。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值