前言
还记得上一节中,框架初始化过程中,涉及到一个比较复杂的方法就是,设置系统初始化器。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 资源加载器,null
this.resourceLoader = resourceLoader;
// 主类,一般是启动类
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 环境监测。判断启动的是 mvc 还是 webflux
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置系统初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 配置 main 函数所在的类
this.mainApplicationClass = deduceMainApplicationClass();
}
复制代码
所以,我们就从系统初始化器开始。
初始化器解析
官方描述:系统初始化器是Spring容器刷新之前执行的一个回调函数。
作用是向Spring Boot容器中注册属性,需要实现ApplicationContextInitializer
接口。
设置初始化器
把系统初始化器设置到 Spring Boot 中,有三种方式。
方式一:spring.factories
定义在srping.factories
中的类,会被 Spring Boot 容器加载。
-
创建初始化器
@Order(1) public class FirstInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { ConfigurableEnvironment environment = applicationContext.getEnvironment(); Map<String, Object> map = new HashMap<>(); map.put("sb", "sb1"); MapPropertySource mapPropertySource = new MapPropertySource("firstInitializer", map); environment.getPropertySources().addLast(mapPropertySource); System.out.println("run firstInitializer"); } } 复制代码
-
配置
spring.factories
路径:
src/main/resources/META-INF/spring.factories
org.springframework.context.ApplicationContextInitializer=\ com.example.indepthspringboot.initializer.FirstInitializer 复制代码
-
启动
方式二:SpringApplication 直接设置
-
创建初始化器
@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("sb2", "sb2"); MapPropertySource mapPropertySource = new MapPropertySource("secondInitializer", map); environment.getPropertySources().addLast(mapPropertySource); System.out.println("run secondInitializer"); } } 复制代码
-
配置
public static void main(String[] args) { //SpringApplication.run(InDepthSpringBootApplication.class, args); SpringApplication springApplication = new SpringApplication(InDepthSpringBootApplication.class); springApplication.addInitializers(new SecondInitializer()); springApplication.run(args); } 复制代码
-
启动
方式三:通过配置文件
-
创建初始化器
@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("sb3", "sb3"); MapPropertySource mapPropertySource = new MapPropertySource("thirdInitializer", map); environment.getPropertySources().addLast(mapPropertySource); System.out.println("run thirdInitializer"); } } 复制代码
-
配置
路径:
src/main/resources/application.properties
context.initializer.classes=com.example.indepthspringboot.initializer.ThirdInitializer 复制代码
-
启动
初始化器加载原理
上面第三种方式,我们发现了非常奇怪的现象,我们定了@Order(3)
的ThirdInitializer
却跑到了第一个输出。
接下来,我们以此为线,查看一下系统初始化器的加载过程。
方式一加载原理
-
设置系统初始化器
设置系统初始化器,是在框架的初始化过程中。
// 设置系统初始化器 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); 复制代码
-
获取类的实例
这里涉及到了,获取类实例的方法,比较复杂。但是也是 Spring Boot 中常用的方法,后面会经常见到。
// 这个方法的作用是:获取指定类的实例private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {});} private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { 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; } 复制代码
-
获取类的实例核心逻辑
我们跳转到
SpringFactoriesLoader.loadFactoryNames(type, classLoader)
中。// SpringFactoriesLoader#loadFactoryNamespublic static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());} 复制代码
这个方法的核心方法是调用
loadSpringFactories(classLoader)
,我们继续深入。对了,这里来先提一下
spring.factories
的格式:org.springframework.context.ApplicationContextInitializer=\ com.example.indepthspringboot.initializer.FirstInitializer,\ com.example.indepthspringboot.initializer.SecondInitializer 复制代码
loadSpringFactories(classLoader)
比较长,但核心逻辑就是从META-INF/spring.factories
中读取类的配置,加载到内存中。其
Map<String, List<String>>
中:key = org.springframework.context.ApplicationContextInitializer
,value = ['com.example.indepthspringboot.initializer.FirstInitializer','com.example.indepthspringboot.initializer.SecondInitializer']
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); // ………… cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } } 复制代码
-
排序
ok,核心逻辑梳理完了,我们拉回主线。
// 这个方法的作用是:获取指定类的实例private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {});} private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // 确保使用名称唯一 // 此时获取到了 META-INF/spring.factories 的配置,不过都是类全路径地址。 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 通过类全路径地址,实例化类 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 根据 @Order() 注解排序 AnnotationAwareOrderComparator.sort(instances); return instances; } 复制代码
至此,我们就了解到方式一的加载方式了。
方式二加载原理
SpringApplication springApplication = new SpringApplication(InDepthSpringBootApplication.class);springApplication.addInitializers(new SecondInitializer());springApplication.run(args);
复制代码
这里我们可以看到,addInitializers()
方式是在 SpringApplication
初始化后调用的,添加到initializers
中的,所以肯定在FirstInitializer
后面。
方式三加载原理
这一种加载方式比较特别,是通过order = 0
的DelegatingApplicationContextInitializer
初始化器调用实现的。
也就是在 context.initializer.classes
属性指定的初始化器,由DelegatingApplicationContextInitializer
调用完成。
public class DelegatingApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered { // NOTE: Similar to org.springframework.web.context.ContextLoader private static final String PROPERTY_NAME = "context.initializer.classes"; private int order = 0; // 初始化器都会被调用initialize()方法 // 而 DelegatingApplicationContextInitializer#initialize(),又是去调用 "context.initializer.classes" 下的初始化器的initialize()方法 @Override public void initialize(ConfigurableApplicationContext context) { ConfigurableEnvironment environment = context.getEnvironment(); List<Class<?>> initializerClasses = getInitializerClasses(environment); if (!initializerClasses.isEmpty()) { applyInitializerClasses(context, initializerClasses); } } // ……}
复制代码
这也就解释了,为什么ThirdInitializer
会在第一位打印。这是因为,通过context.initializer.classes
设置的初始化类,会在order = 0 的初始化类中被调用。
系统初始化器的调用
系统初始化器的调用是在,准备上下文方法中:SpringApplication # prepareContext() # applyInitializers()
。
protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }
复制代码
这里也就是遍历initializers
属性,去调用initialize()
方法了,无需赘述。
小结
这一节主要是,系统初始化器相关内容。
作为学习 Spring Boot 启动的第一篇细节文章,本章除了系统初始化器外,还有个重点,就是方式一加载原理中的类加载机制。
类加载机制,在后面都会频繁用到,后面就不再细述了。