Springboot之应用初始化——ApplicationContextInitializer.class

1. 先介绍ApplicationContextInitializer.class

ApplicationContextInitializer是Spring提供的用于应用启动时回调的接口,需要实现此接口的initializeinitialize(ConfigurableApplicationContext applicationContext)方法。当Spring启动(即执行run方法)并初始化环境配置以后,完成应用初始化以前,会回调所有ApplicationContextInitializer的子类(当然是已经配置了的子类)。

2. 怎么用呢?

2.1. 实现接口

这没啥好说的,就看示例吧:

package com.jwb.demo.initializer;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;

@Order(2) // 如果有多个实体类,可以配置的顺序,此注解非必须的
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
	public MyApplicationContextInitializer() {
		System.out.println("MyApplicationContextInitializer 构造方法。");
	}

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		System.out.println("MyApplicationContextInitializer 的 initialize()方法被回调了。");
	}
}

2.2. 将实现好的类配置到环境中

一般看源码,一边说配置方法,做到知其然更知其所以然。
话不多说,上源码:

2.2.1. spring加载默认配置中指定的实体类

先看看ApplicationContextInitializer的子类何时被实例化的。

public class SpringApplication {
		// SpringApplication 的构造方法
		public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
			this.resourceLoader = resourceLoader;
			Assert.notNull(primarySources, "PrimarySources must not be null");
			this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
			this.webApplicationType = deduceWebApplicationType();
			/// 在这里实例化了ApplicationContextInitializer的子类/
			/// 在这里实例化了ApplicationContextInitializer的子类/
			/// 在这里实例化了ApplicationContextInitializer的子类/
			// getSpringFactoriesInstances()方法是,在文件META-INF/spring.factories读取配置好的实体的全限类名,并将这些类一一的实例化
			setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
			/// 在这里实例化了ApplicationContextInitializer的子类/
			/// 在这里实例化了ApplicationContextInitializer的子类/
			/// 在这里实例化了ApplicationContextInitializer的子类/
			setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
			this.mainApplicationClass = deduceMainApplicationClass();
		}
		
		// 添加Initializer到环境中的函数
		public void setInitializers(
			Collection<? extends ApplicationContextInitializer<?>> initializers) {
			//	private List<ApplicationContextInitializer<?>> initializers; 这是成员变量
			this.initializers = new ArrayList<>();
			this.initializers.addAll(initializers); // 实际上就把这些实例化后的Initializer添加到了List添中
	}
}
2.2.1.1. 小结

看了源码过后,就搞明白了什么时候实例化这些Initializer并保存到了哪里去了。那么怎么配置就很好理解了,相信不用教都会了。
spring在此种情况下加载的实体类,是配置在文件META-INF/spring.factories中配置好的,在文件中加入配置org.springframework.context.ApplicationContextInitializer=xxx.xxx.xxx.myApplicationContextInitializer。如果想了解加载过程,可以看看我的另外一篇文章,里边有说到关于getSpringFactoriesInstances()方法是如何加载实体类的。链接

2.2.2. 在项目配置(application.properties)文件中添加配置

先不说怎么配置,还是先看看,springboot是如何从application.properties加载到你配置的实体类的呢?
结合上文提到的,应用会在实例化SpringApplication的时候,加载文件META-INF/spring.factories中配置好的默认实体类,那么就有这么一个实体类,负责加载application.properties文件中所配置的实体类。
下面还是通过源码解读,看看实体类怎么加载的,实例化以后又做了那些事呢?

META-INF/spring.factories文件中的部分内容如下:
在这里插入图片描述
在这里插入图片描述

我们来看DelegatingApplicationContextInitializer这个实体类,就它,就这个了,就它负责加载application.properties中配置的Initializer实体类的。上源码看看:

public class DelegatingApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
		private static final String PROPERTY_NAME = "context.initializer.classes";
		private int order = 0;
		@Override
		public void initialize(ConfigurableApplicationContext context) {
			ConfigurableEnvironment environment = context.getEnvironment();
			List<Class<?>> initializerClasses = getInitializerClasses(environment);
			if (!initializerClasses.isEmpty()) {
				applyInitializerClasses(context, initializerClasses);
			}
		}
	
		private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
			String classNames = env.getProperty(PROPERTY_NAME);
			List<Class<?>> classes = new ArrayList<>();
			if (StringUtils.hasLength(classNames)) {
				for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
					classes.add(getInitializerClass(className));
				}
			}
			return classes;
		}
	
		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);
			}
		}
	
		private void applyInitializerClasses(ConfigurableApplicationContext context,
				List<Class<?>> initializerClasses) {
			Class<?> contextClass = context.getClass();
			List<ApplicationContextInitializer<?>> initializers = new ArrayList<>();
			for (Class<?> initializerClass : initializerClasses) {
				initializers.add(instantiateInitializer(contextClass, initializerClass));
			}
			applyInitializers(context, initializers);
		}
	
		private ApplicationContextInitializer<?> instantiateInitializer(Class<?> contextClass,
				Class<?> initializerClass) {
			Class<?> requireContextClass = GenericTypeResolver.resolveTypeArgument(
					initializerClass, ApplicationContextInitializer.class);
			Assert.isAssignable(requireContextClass, contextClass,
					String.format(
							"Could not add context initializer [%s]"
									+ " as its generic parameter [%s] is not assignable "
									+ "from the type of application context used by this "
									+ "context loader [%s]: ",
							initializerClass.getName(), requireContextClass.getName(),
							contextClass.getName()));
			return (ApplicationContextInitializer<?>) BeanUtils
					.instantiateClass(initializerClass);
		}
	
		@SuppressWarnings({ "unchecked", "rawtypes" })
		private void applyInitializers(ConfigurableApplicationContext context,
				List<ApplicationContextInitializer<?>> initializers) {
			initializers.sort(new AnnotationAwareOrderComparator());
			for (ApplicationContextInitializer initializer : initializers) {
				initializer.initialize(context);
			}
		}
	
		public void setOrder(int order) {
			this.order = order;
		}
	
		@Override
		public int getOrder() {
			return this.order;
		}
}
2.2.2.1. 小结

什么?代码很长,看懵了!没关系,总结一下就好了。
先看看initialize()方法,所有的Initializer都必须实现的方法,spring会在初始化过程中调用的。那么就从这里开始看吧。
主要看getInitializerClasses()方法, 此方法中作了两件事情,读取配置文件和应用初始化器(Initializer)。

2.2.2.1.1. 读取配置文件(哈哈,重点来了,这里就会说到怎么配置)

String classNames = env.getProperty(PROPERTY_NAME);这里看出,在读取配置文件中key为context.initializer.classes的值。嘿嘿,对,你的直觉是对的,在application.properties中加入这样的配置
context.initializer.classes=com.jwb.demo.initializer.MyApplicationContextInitializer
就可以将你的自定义的实现类配置上了,多个Initializer的全限类名可以用英文的逗号分隔","。

2.2.2.1.2. 运用Initializer

那么实例化好了这些Initializer之后呢,来看看
applyInitializers()方法的内部关键代码

	for (ApplicationContextInitializer initializer : initializers) {
		initializer.initialize(context);
	}

刚实例化好的,我们配置的自定义Initializer,都便利了,并调用了initialize()方法,没错,就是你必须且唯一实现的那个方法。

2.2.3. 在run方法之前添加

如果你理解了上文说到的第一种配置方法,那么这种方法添加Initializer简直就是太好理解了。来,直接上示例

package com.jwb.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.jwb.demo.initializer.MyApplicationContextInitializer;

@SpringBootApplication
public class GenDemoApplication {

	public static void main(String[] args) {
		SpringApplication springApplication = new SpringApplication(GenDemoApplication.class);
		springApplication.addInitializers(new MyApplicationContextInitializer()); // 添加Initializer
		springApplication.run(args);
	}
}

咱都知道,所有的Initializer的initialize()方法,都是再初始化过程中被调用的,也就是再springApplication.run(args);这句代码执行的时候被调用的。
那么,再拿被执行之前添加到SpringApplication中的成员变量initializers里就可以了,还记得吗,成员变量private List<ApplicationContextInitializer<?>> initializers; 即使一个List。
看看源码,是不是这样的:

	public void addInitializers(ApplicationContextInitializer<?>... initializers) {
		this.initializers.addAll(Arrays.asList(initializers));
	}

简单吧~~~~~~~~~~~~~~~~~~~~~ !!!!

3. 关于order

我们可以看到,DelegatingApplicationContextInitializer还实现了Order接口,这个Order是spring在多个默认Initializer中执行循序的排序,这个类的order是0,数字越小就越先执行。那么,我们写的实现类中,如果@Order注解是0,会不会导致先执行我们自定义的application文件中定义的Initializer之后,才执行这个类呢?
答案是:----------绝对不会的 ----------- 。为什么?因为儿子绝对不会再父亲之前出生的,哈哈!多体会体会,你就能明白了。
咱自定义的Initializer的order顺序,只会影响到所有由DelegatingApplicationContextInitializer从application文件中加载的类的执行顺序。

但是,但是,但是。。。。。。。

如果你用META-INF/spring.factories的配置方式,嘿嘿,就会有可能先执行咱自定义的Initializer。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值