赞
踩
ApplicationContextInitializer接口的文档是这么写的:
1、ApplicationContextInitializer是一个回调接口,用于在ConfigurableApplicationContext#refresh()执行刷新之前初始化ConfigurableApplicationContext。SpringApplication#prepareContext()方法会执行ApplicationContextInitializer实现类的initialize方法。
2、通常在需要对应用程序上下文进行一些编程初始化的Web应用程序中使用,例如对ConfigurableApplicationContext#getEnvironment()注册属性源或者激活配置文件。
3、鼓励系统初始化器去实现org.springframework.core.Ordered Ordered接口或者使用@Order注解,以便在调用之前对实例进行排序。
若不举例说明,很难理解ApplicationContextInitializer接口的文档说的是什么意思。那现在就开始介绍系统初始化器的3种使用方式吧。
1、新建FirstInitializer实现ApplicationContextInitializer接口
- /**
- * 使用@Order注解(第3点)
- */
- @Order(1)
- public class FirstInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
-
- /**
- * 在resources目录下新建/META-INF/spring.factories目录与文件。
- * 在resources/META-INF/spring.factories配置:
- * org.springframework.context.ApplicationContextInitializer=com.example.springbootdemo.FirstInitializer
- *
- * spring.factories的配置规则是: 接口全名=实现类全名
- */
- @Override
- public void initialize(ConfigurableApplicationContext applicationContext) {
- /**
- * 本方法代码是添加属性 key1=value1。
- *
- * 可通过以下方式获取添加的属性:
- * @Value("${key1}")
- * String key1;
- */
- ConfigurableEnvironment environment = applicationContext.getEnvironment();
- Map<String, Object> map = new HashMap<>();
- map.put("key1", "value1");
-
- // 新建属性源,并添加属性源。(第2点)
- MapPropertySource mps = new MapPropertySource("firstInitializer", map);
- environment.getPropertySources().addLast(mps);
-
- System.out.println("#############FirstInitializer.initialize 运行");
- }
-
- }
2、在resources目录下新建/META-INF/spring.factories目录与文件
在spring.factories中添加
org.springframework.context.ApplicationContextInitializer=com.example.springbootdemo.FirstInitializer
3、获取属性值的方式如下:
@Value("${key1}")
String key1;
4、启动工程,就能获取值了。
5、不使用@Order,通过实现Ordered接口实现排序。新建FirstOrderedInitializer.java
- /**
- * 通过实现Ordered接口实现排序
- */
- public class FirstOrderedInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
-
- @Override
- public void initialize(ConfigurableApplicationContext applicationContext) {
- System.out.println("#############FirstOrderedInitializer.initialize 运行");
- }
-
- // 返回排序值
- @Override
- public int getOrder() {
- return 11;
- }
- }
6、在spring.factories中添加
org.springframework.context.ApplicationContextInitializer=com.example.springbootdemo.FirstInitializer,\
com.example.springbootdemo.FirstOrderedInitializer
7、运行程序,控制台输出
#############FirstInitializer.initialize 运行
#############FirstOrderedInitializer.initialize 运行
FirstInitializer使用@Order注解排序值是1,FirstOrderedInitializer#getOrder()返回11。由此可知:排序值越小越先执行。
1、新建SecondInitializer.java
- @Order(2)
- public class SecondInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
-
- @Override
- public void initialize(ConfigurableApplicationContext applicationContext) {
-
- ConfigurableEnvironment environment = applicationContext.getEnvironment();
- Map<String, Object> map = new HashMap<>();
- map.put("key2", "value2");
-
- MapPropertySource mps = new MapPropertySource("secondInitializer", map);
- environment.getPropertySources().addLast(mps);
-
- System.out.println("#############SecondInitializer.initialize 运行");
- }
- }
2、修改启动类,通过代码添加SecondInitializer
- @SpringBootApplication
- public class SpringBootDemoApplication {
-
- public static void main(String[] args) {
-
- //SpringApplication.run(SpringBootDemoApplication.class, args);
-
- // 第2中添加系统初始化器的方式
- SpringApplication springApplication = new SpringApplication(SpringBootDemoApplication.class);
- springApplication.addInitializers(new SecondInitializer());
- springApplication.run(args);
- }
-
- }
1、新建ThirdInitializer.java
- @Order(3)
- public class ThirdInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
-
- @Override
- public void initialize(ConfigurableApplicationContext applicationContext) {
-
- ConfigurableEnvironment environment = applicationContext.getEnvironment();
- Map<String, Object> map = new HashMap<>();
- map.put("key3", "value3");
-
- MapPropertySource mps = new MapPropertySource("thirdInitializer", map);
- environment.getPropertySources().addLast(mps);
-
- System.out.println("#############ThirdInitializer.initialize 运行");
- }
- }
2、在application.properties中配置
context.initializer.classes=com.example.springbootdemo.ThirdInitializer
3、启动工程,控制台有以下输出
#############ThirdInitializer.initialize 运行
#############FirstInitializer.initialize 运行
#############SecondInitializer.initialize 运行
#############FirstOrderedInitializer.initialize 运行
ThirdInitializer的注解是@Order(3),但是ThirdInitializer比FirstInitializer、SecondInitializer先运行。是不是使用context.initializer.classes配置的初始化器就会先运行呢?后面会回答这问题。
spring boot 版本是2.2.4.RELEASE。
先把启动类SpringBootDemoApplication.java的代码还原为
SpringApplication.run(SpringBootDemoApplication.class, args);
使用最常规的启动方式来分析spring boot 的源码。
1、进入run方法内,直到看见如下代码:
- // 源码位置org.springframework.boot.SpringApplication#run(java.lang.Class<?>[], java.lang.String[])
- public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
- return new SpringApplication(primarySources).run(args);
- }
可粗略的得出一个结论,spring boot 使用一句代码 SpringApplication.run(SpringBootDemoApplication.class, args); 启动工程,本质上是创建SpringApplication实例,再运行实例的run方法。run方法执行完,工程就启动完成了。
1.1、继续debug,进入SpringApplication的构造函数中
- // 源码位置 org.springframework.boot.SpringApplication#SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class<?>...)
- 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 = WebApplicationType.deduceFromClasspath();
- // 这行代码就是设置初始化器
- setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
- setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
- this.mainApplicationClass = deduceMainApplicationClass();
- }
1.1.1、setInitializers方法源码如下:
- // 源码位置 org.springframework.boot.SpringApplication#setInitializers
- public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
- /**
- * 系统初始化器可以有多个,形成一个系统初始化器列表。
- * 在创建SpringApplication对象时,将系统初始化器列表赋值给SpringApplication的initializers属性。
- */
- this.initializers = new ArrayList<>(initializers);
- }
1.1.2、getSpringFactoriesInstances(ApplicationListener.class)是获取系统初始化器的函数
进入方法体中debug
- // 源码位置 org.springframework.boot.SpringApplication#getSpringFactoriesInstances(java.lang.Class<T>, java.lang.Class<?>[], java.lang.Object...)
- private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
- ClassLoader classLoader = getClassLoader();
- // Use names and ensure unique to protect against duplicates
- Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
- List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
- AnnotationAwareOrderComparator.sort(instances);
- return instances;
- }
1.1.2.1、ClassLoader classLoader = getClassLoader();
getClassLoader()返回结果是AppClassLoader实例。
Java自带了3个类加载器:BootstrapClassLoader、ExtClassLoader、AppClassLoader。AppClassLoader会加载classpath路径下jar包和目录的class文件。我们自己编写的代码以及代码依赖的第三方jar包通常都是由它来加载的。
可运行System.getProperty("java.class.path"),查看当前工程的classpath是什么。
1.1.2.2、Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
type是ApplicationContextInitializer接口,这句代码是获取type接口实现类的类全名集合。进入到SpringFactoriesLoader.loadFactoryNames方法内部。
- // 源码位置 org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
- public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
- String factoryTypeName = factoryType.getName();
- /**
- * 重点代码是 loadSpringFactories(classLoader)。
- * 先讲结论,loadSpringFactories(classLoader)返回一个LinkedMultiValueMap对象,此对象一个key对应多个value。
- * key是接口全类名,value是接口的实现类全类名,value可以有多个。
- */
- return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
- }
debug到loadSpringFactories(classLoader)内部
- // 源码位置 org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
- private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
- MultiValueMap<String, String> result = cache.get(classLoader);
- if (result != null) {
- return result;
- }
-
- try {
- /**
- * FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
- * 通过AppClassLoader加载Classpath下的META-INF/spring.factories
- * 当前工程和依赖的第三方jar包中的META-INF/spring.factories都会被加载进来
- */
- Enumeration<URL> urls = (classLoader != null ?
- classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
- ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
- result = new LinkedMultiValueMap<>();
- while (urls.hasMoreElements()) {
- URL url = urls.nextElement();
- // resource是包含了spring.factories文件路径的资源对象
- UrlResource resource = new UrlResource(url);
- // 使用spring.factories中配置的键值对创建Properties对象
- Properties properties = PropertiesLoaderUtils.loadProperties(resource);
- // 循环properties的键值对
- for (Map.Entry<?, ?> entry : properties.entrySet()) {
- String factoryTypeName = ((String) entry.getKey()).trim();
- for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
- /**
- * factoryTypeName是spring.factories中配置的key,比如:ApplicationContextInitializer接口全类名
- * factoryImplementationName是key对应的value,比如:ApplicationContextInitializer实现类FirstInitializer的全类名
- */
- result.add(factoryTypeName, factoryImplementationName.trim());
- }
- }
- }
- cache.put(classLoader, result);
-
- /**
- * result是一个LinkedMultiValueMap。key是接口全名,value是接口的实现类全类名,value可以是多个。
- * 需要注意的是:
- * 1、AppClassLoader会获取Classpath下所有jar包的spring.factories文件
- * 2、并将spring.factories中所有的配置添加到result中
- * 基于以上两点,就不难理解为什么要用缓存了
- * cache.put(classLoader, result); 、 MultiValueMap<String, String> result = cache.get(classLoader);
- * AppClassLoader把classpath下所有的spring.factories读取到缓存中。下次要用spring.factories配置时,从缓存中拿就好了
- */
- return result;
- }
- catch (IOException ex) {
- throw new IllegalArgumentException("Unable to load factories from location [" +
- FACTORIES_RESOURCE_LOCATION + "]", ex);
- }
- }
在此总结下 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); 这行代码的作用。大致分为以下2个小步骤:
1、使用AppClassLoader获取Classpath下的spring.factories文件的路径
2、读取spring.factories并创建properties对象
3、将type接口(本文中type是ApplicationContextInitializer)实现类的类全名返回。第三方jar包spring.factories中配置的ApplicationContextInitializer实现类的全类名也会被读取并返回,所以结果是Set<String>类型。
1.1.2.3、List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
获得了系统初始化的全类名集合,就能创建系统初始化器实例集合了,源码如下:
- // 源码位置 org.springframework.boot.SpringApplication#createSpringFactoriesInstances
- private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
- ClassLoader classLoader, Object[] args, Set<String> names) {
- List<T> instances = new ArrayList<>(names.size());
- for (String name : names) {
- try {
- // AppClassLoader使用类全名把类加载到jvm内存中
- Class<?> instanceClass = ClassUtils.forName(name, classLoader);
- Assert.isAssignable(type, instanceClass);
- // 获取系统初始化器的构造器
- Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
- // 通过构造器创建实例
- T instance = (T) BeanUtils.instantiateClass(constructor, args);
- // 添加到集合中
- instances.add(instance);
- }
- catch (Throwable ex) {
- throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
- }
- }
- // 返回系统初始化器实例集合
- return instances;
- }
1.1.2.4、AnnotationAwareOrderComparator.sort(instances); 这是对系统初始化器实例集合进行排序。
debug到如下代码内
- // 源码位置 org.springframework.core.OrderComparator#doCompare()
- private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
- boolean p1 = (o1 instanceof PriorityOrdered);
- boolean p2 = (o2 instanceof PriorityOrdered);
- if (p1 && !p2) {
- return -1;
- }
- else if (p2 && !p1) {
- return 1;
- }
-
- /**
- * getOrder方法很复杂,本文只关注getOrder方法的两个作用。
- * 1、获取@Order注解值
- * 2、获取Ordered接口getOrder方法返回值
- */
- int i1 = getOrder(o1, sourceProvider);
- int i2 = getOrder(o2, sourceProvider);
-
- // 排序规则是升序
- return Integer.compare(i1, i2);
- }
在以下代码中打上断点,运行程序debug
com.example.springbootdemo.FirstOrderedInitializer#getOrder()
// 调用Ordered实现类的getOrder()方法,获取排序值
return 2;
org.springframework.core.annotation.OrderUtils#findOrder()
// getOrder方法获取@Order注解的值
MergedAnnotation<Order> orderAnnotation = annotations.get(Order.class);
if (orderAnnotation.isPresent()) {
return orderAnnotation.getInt(MergedAnnotation.VALUE);
}
至此,系统初始化器的初始化代码就讲完了。重点是getSpringFactoriesInstances方法,下面对此方法做总结:
- // 源码位置 org.springframework.boot.SpringApplication#getSpringFactoriesInstances(java.lang.Class<T>, java.lang.Class<?>[], java.lang.Object...)
- private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
- // getClassLoader()返回AppClassLoader
- ClassLoader classLoader = getClassLoader();
- // 获取系统初始化器全类名集合
- Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
- // 创建系统初始化器集合
- List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
- // 对系统初始化器排序
- AnnotationAwareOrderComparator.sort(instances);
- // 返回排序后的系统初始化器集合
- return instances;
- }
FirstInitializer添加到SpringApplication.initializers的原理已经讲清楚了。
SecondInitializer添加给SpringApplication.initializers的方式就很简单了,springApplication.addInitializers(new SecondInitializer()); 的源码如下:
- // 源码位置 org.springframework.boot.SpringApplication#addInitializers
- public void addInitializers(ApplicationContextInitializer<?>... initializers) {
- this.initializers.addAll(Arrays.asList(initializers));
- }
目前遗留下两个问题:
1、SecondInitializer:addAll是将SecondInitializer添加到this.initializers的末尾,排序位置不对。
2、ThirdInitializer通过application.properties配置,还没被添加到this.initializers中。
带着这两个疑问,我们来探究 new SpringApplication(primarySources).run(args); 的run方法,即SpringApplication的运行阶段。
使用 SpringApplication.run(SpringBootDemoApplication.class, args); 启动程序。在源码中是执行 new SpringApplication(primarySources).run(args); 这句代码跟前面介绍第二种配置系统初始化器的代码很相似,为了配置SecondInitializer,需要把启动类的代码改成
- @SpringBootApplication
- public class SpringBootDemoApplication {
-
- public static void main(String[] args) {
-
- //SpringApplication.run(SpringBootDemoApplication.class, args);
-
- // 第2中添加系统初始化器的方式
- SpringApplication springApplication = new SpringApplication(SpringBootDemoApplication.class);
- springApplication.addInitializers(new SecondInitializer());
- springApplication.run(args); // 这句代码打上断点调试
- }
-
- }
在 springApplication.run(args); 这句代码打上断点,启动工程、调试。
debug到run方法内
- // 源码位置 org.springframework.boot.SpringApplication.run(java.lang.String...)
- public ConfigurableApplicationContext run(String... args) {
- /**
- * run方法很复杂,本文作为spring boot源码系列的第一篇博客,仅关注系统初始化器相关代码。其他代码忽略
- * prepareContext方法内会运行系统初始化器
- */
- prepareContext(context, environment, listeners, applicationArguments, printedBanner);
- // ApplicationContextInitializer接口文档第三点
- refreshContext(context);
- }
debug到prepareContext方法中
- // 源码位置 org.springframework.boot.SpringApplication.prepareContext
- private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
- SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
- // 应用初始化器
- applyInitializers(context);
-
- }
applyInitializers方法源码:
- // 源码位置 org.springframework.boot.SpringApplication#applyInitializers
- protected void applyInitializers(ConfigurableApplicationContext context) {
- /**
- * getInitializers()获取系统初始化器,并且对系统系统初始化器再次排序,解决了SecondInitializer的排序问题
- * 循环系统初始化器,并执行系统初始化器的initialize方法
- */
- for (ApplicationContextInitializer initializer : getInitializers()) {
- Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
- ApplicationContextInitializer.class);
- Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
- initializer.initialize(context);
- }
- }
1、getInitializers()获取系统初始化器,并且对系统系统初始化器再次排序
- // 源码位置 org.springframework.boot.SpringApplication#asUnmodifiableOrderedSet
- private static <E> Set<E> asUnmodifiableOrderedSet(Collection<E> elements) {
- List<E> list = new ArrayList<>(elements);
- // 再次对系统初始化器集合排序
- list.sort(AnnotationAwareOrderComparator.INSTANCE);
- return new LinkedHashSet<>(list);
- }
2、getInitializers()获得系统初始化器集合,排在第一位的系统初始化器是DelegatingApplicationContextInitializer(委派系统初始化器,Order值是0),它会将初始化操作委派给环境属性context.initializer.classes指定的初始化器。
DelegatingApplicationContextInitializer.initialize方法源码如下
- // 源码位置org.springframework.boot.context.config.DelegatingApplicationContextInitializer#initialize
- public void initialize(ConfigurableApplicationContext context) {
- // 获取环境变量
- ConfigurableEnvironment environment = context.getEnvironment();
- // 获取context.initializer.classes配置的初始化器集合
- List<Class<?>> initializerClasses = getInitializerClasses(environment);
- if (!initializerClasses.isEmpty()) {
- // 执行初始化器的initialize方法
- applyInitializerClasses(context, initializerClasses);
- }
- }
2.1 getInitializerClasses(environment); 源码分析
- // 源码位置 org.springframework.boot.context.config.DelegatingApplicationContextInitializer#getInitializerClasses
- private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
- // PROPERTY_NAME = "context.initializer.classes"
- String classNames = env.getProperty(PROPERTY_NAME);
- List<Class<?>> classes = new ArrayList<>();
- if (StringUtils.hasLength(classNames)) {
- // 通过","分隔context.initializer.classes的值
- for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
- // getInitializerClass(className)使用AppClassLoader加载类
- classes.add(getInitializerClass(className));
- }
- }
- return classes;
- }
2.2、applyInitializerClasses(context, initializerClasses);源码分析
- // 源码位置 org.springframework.boot.context.config.DelegatingApplicationContextInitializer#applyInitializerClasses
- private void applyInitializerClasses(ConfigurableApplicationContext context, List<Class<?>> initializerClasses) {
- Class<?> contextClass = context.getClass();
- List<ApplicationContextInitializer<?>> initializers = new ArrayList<>();
- // initializerClasses是context.initializer.classes指定的类集合
- for (Class<?> initializerClass : initializerClasses) {
- // 实例化系统初始化器
- initializers.add(instantiateInitializer(contextClass, initializerClass));
- }
- // 应用系统初始化器
- applyInitializers(context, initializers);
- }
2.2.1 applyInitializers(context, initializers); 源码分析
- // 源码位置 org.springframework.boot.context.config.DelegatingApplicationContextInitializer#applyInitializers
- private void applyInitializers(ConfigurableApplicationContext context,
- List<ApplicationContextInitializer<?>> initializers) {
- // 先排序,这里是对context.initializer.classes配置的系统初始化器进行排序
- initializers.sort(new AnnotationAwareOrderComparator());
- for (ApplicationContextInitializer initializer : initializers) {
- // 执行初始化器的initialize方法,会进入 ThirdInitializer#initialize 方法中
- initializer.initialize(context);
- }
- }
ThirdInitializer就被调用了。现在对DelegatingApplicationContextInitializer(委派系统初始化器)做个总结:
1、DelegatingApplicationContextInitializer的排序值是0,比FirstInitializer、SecondInitializer的排序值小,最先被调用。
2、DelegatingApplicationContextInitializer实例化context.initializer.classes配置的系统初始化器,并运行这些系统初始化器的initialize方法。所以context.initializer.classes配置的系统初始化器会先运行。
3、DelegatingApplicationContextInitializer的排序值是0,若把FirstInitializer的排序值设置为小于0,则FirstInitializer会先运行。
讲完了ThirdInitializer的实例化、运行过程。在回到 org.springframework.boot.SpringApplication#applyInitializers方法中,继续讲解FirstInitializer、FirstOrderedInitializer、SecondInitializer的运行过程
- // 源码位置 org.springframework.boot.SpringApplication#applyInitializers
- protected void applyInitializers(ConfigurableApplicationContext context) {
- /**
- * 循环处理getInitializers()返回的系统初始化器,
- * 排在第一位的是DelegatingApplicationContextInitializer,前面已经讲了此初始化器的运行过程
- * 后面几次循环获得的initializer包含FirstInitializer、FirstOrderedInitializer、SecondInitializer
- * initializer.initialize(context);就直接运行我们自定义的系统初始化器了
- */
- for (ApplicationContextInitializer initializer : getInitializers()) {
- Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
- ApplicationContextInitializer.class);
- Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
- initializer.initialize(context);
- }
- }
好了,spring boot 2系统初始化器的使用与源码都讲完了,后面我会继续更新 spring boot 源码系列 ,欢迎大家来一起交流。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。