系统初始化器介绍
实现 ApplicationContextInitializer 接口的类称为系统初始化器,在容器刷新前(refresh())执行其回调方法(initialize()),用于向 SpringBoot 容器中注册属性(通常用于 WEB 环境中)。
自定义初始化器注入环境变量
新建三个类 FirstInitializer、SecondInitializer 和 ThirdInitializer 实现 ApplicationContextInitializer,使用 @Order 注解标记其顺序,分别为 1、2 和 3, 在 initialize 方法中为环境添加自定义属性。
@Order(1)
public class FirstInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
var environment = applicationContext.getEnvironment();
var map = Map.<String, Object>of("key1", "value1");
var mapPropertySource = new MapPropertySource("firstInitializer", map);
environment.getPropertySources().addLast(mapPropertySource);
System.out.println("run firstInitializer");
}
}
注册方式一、在 resources 文件夹中新建文件 META-INF/spring.factories,注册 FirstInitializer。
org.springframework.context.ApplicationContextInitializer=store.xianglin.sb2.Initializer.FirstInitializer
注册方式二、通过调用 SpringApplication.addInitializers()
方法注册 SecondInitializer。
@SpringBootApplication
@MapperScan("store.xianglin.sb2.mapper")
public class SpringBoot2Application {
public static void main(String[] args) {
// SpringApplication.run(SpringBoot2Application.class, args);
var springApplication = new SpringApplication(SpringBoot2Application.class);
springApplication.addInitializers(new SecondInitializer());
springApplication.run(args);
}
}
注册方式三、通过配置文件注册 ThirdInitializer。
context.initializer.classes=store.xianglin.sb2.Initializer.ThirdInitializer
SpringFactoriesLoader 介绍
org.springframework.core.io.support.SpringFactoriesLoader
类介绍如下。
- 它是框架内部使用的通用工厂加载机制
- 它从 classpath 下的多个 jar 包中读取 META-INF/spring.factories 文件,并加载和初始化文件中定义的类
- spring.factories 文件内容必须是 Properties 格式(key-value)
- key 是接口或者抽象类的全限定名,value 是实现类,多个实现类用
,
分隔
loadFactories 流程
以 ApplicationContextInitializer 加载为例,loadFactories 流程见 SpringApplication 中 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
// 从类路径下加载所有 ApplicationContextInitializer 的实现类
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 实例化所有 ApplicationContextInitializer 对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 按 Order 接口或注解定义的顺序排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
在 loadFactoryNames 方法中获取 ApplicationContextInitializer 实现类分为两步,首先加载所有 META-INF/spring.factories 文件的内容,以 factoryClassName 分组保存,然后获取 ApplicationContextInitializer 对应的实现类。
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader)
.getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 缓存已加载的结果
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
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();
// 遍历找到的 spring.factories 文件,读取并转成 Properties 对象
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// 遍历 Properties 中的属性,将 value 按逗号拆分,并添加到 result 中
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
// 缓存加载结果
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
获取到 ApplicationContextInitializer 的实现类后,通过全限定名加载类,然后通过反射创建对象。
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 {
// 通过类名加载类
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
// 确保 spring.factories 中定义的类是 ApplicationContextInitializer 的实现类
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;
}
系统初始化器原理
系统初始化器有三种注册方法,通过 spring.factories 文件注册和编码调用 addInitializers() 方法注册都会将实现类对象添加到 private List<ApplicationContextInitializer<?>> initializers;
中,其调用是在 org.springframework.boot.SpringApplication#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.");
// 调用初始化器的 initialize 方法,前面实现的注入环境变量在这里执行
initializer.initialize(context);
}
}
从上图可以发现,通过配置文件注册的 ThirdInitializer 并不在 initializers 中,其加载调用是通过 DelegatingApplicationContextInitializer 类完成的。
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()) {
// 执行初始化器逻辑,由此可发现:配置文件注册的初始化器执行顺序取决于 DelegatingApplicationContextInitializer 的顺序
applyInitializerClasses(context, initializerClasses);
}
}
}
而 DelegatingApplicationContextInitializer 是在 spring-boot-x.x.x.jar!/META-INF/spring.factories
中注册的。