总文档 :文章目录
Github : github.com/black-ant
一 . 前言
之前说了 SpringBoot 自动配置的地方 , 其中就涉及到 Factories 的加载 , 这一篇详细的把这个流程过一下.
Factories 的作用是工厂类的元数据 , Spring 会通过 .factories 文件将需要初始化的类反射出来
二 . 核心类
Factories 流程中核心的类是 SpringFactoriesLoader , 在 SpringApplication run 时 , 即会加载 Factories相关属性
- spring.factories 配置文件 : Spring 自己的一套 SPI 机制的配置文件
- SpringFactoriesLoader 类,用于加载 spring.factories 配置文件
先看一下 SpringFactoriesLoader 主要流程
SpringFactoriesLoader
SF- FACTORIES_RESOURCE_LOCATION :
SF- Map<ClassLoader,MultivalueMap> cache
M- loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader)
|- 获得接口的类名 -> factoryClass.getName()
|- 加载 FACTORIES_RESOURCE_LOCATION 配置文件,获得接口对应的实现类名们
|-> loadSpringFactories
M- loadSpringFactories(@Nullable ClassLoader classLoader)
|- 缓存已存在 , 则直接返回
|- 获得 FACTORIES_RESOURCE_LOCATION 对应的 URL 们
|- 创建 LinkedMultiValueMap<String, String> 对象 = result
|- 遍历 URL 数组
|- 获得 URL = url
|- 创建 UrlResource 对象(url)
|- 加载 "META-INF/spring.factories" 配置文件,成为 Properties 对象
|- 遍历 Properties 对象
|- 使用逗号分隔
|- 添加到 result 中
|- 加到 cache 中
M- loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader)
?- 获得接口对应的实现类名们,然后创建对应的对象们
|- 获得 ClassLoader -> SpringFactoriesLoader.class.getClassLoader()
|- 获得接口对应的实现类名们 -> loadFactoryNames
|- 遍历 factoryNames 数组,创建实现类的对象 -> for + result.add
|- 排序 -> AnnotationAwareOrderComparator.sort(result);
M- instantiateFactory
|- 获得 Class 类
|- 判断是否实现了指定接口
|- 创建对象
复制代码
三 . 流程
3.1 起点
factories 加载的起点就在 SpringApplication 中.
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//获取 ApplicationContextInitializer 的 Factories
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//获取 ApplicationListener 的 Factories
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
复制代码
3.2 Application 的 Factories 加载流程
C1- SpringApplication
M1_01- getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args)
?- 获取 SpringFactories 的实例
- 获取一个 ClassLoader , 这里就是常规的 AppClassLoader
- 通过 SpringFactoriesLoader 加载 FactoryNames
- createSpringFactoriesInstances 创建实例 -> M1_02
- 排序后返回
M1_02- createSpringFactoriesInstances
- ClassUtils.forName 获取反射类 , BeanUtils.instantiateClass 创建实例
C2- SpringFactoriesLoader
F01- Map<ClassLoader, MultiValueMap<String, String>> cache
M2_01- loadFactoryNames
- factoryClass.getName() : 获得接口的类名 -> PS_M2_01_1
- loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList())
?- 加载 FACTORIES_RESOURCE_LOCATION 配置文件,获得接口对应的实现类名们
M2_02- loadSpringFactories(@Nullable ClassLoader classLoader)
?- 加载 FACTORIES_RESOURCE_LOCATION 配置文件,获得接口对应的实现类名们
- 首先从 cache (F01)中获取 MultiValueMap , 存在直接返回
- 如果 ClassLoader 存在 , classLoader.getResources(FACTORIES_RESOURCE_LOCATION)
- 如果 ClassLoader 不存在 , ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)
?- PS_M2_02_1 , 获取文件资源迭代器
- 迭代加载 , 先获取 UrlResource , 再获取 Properties
FOR- 迭代 Properties 属性 , 添加到 Map 中 ,放入缓存
?- PS_M2_02_2 添加详情
// PS_M2_01_1 : 此处参数为 org.springframework.context.ApplicationContextInitializer
// PS_M2_02_1 : location
- FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
- location 不止一个包中有 , 每个包中都可以有 spring.factories , 他们会循环迭代
// M2_02 伪代码
Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader){
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
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();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
复制代码
PS_M2_02_2 详情:
result.add(factoryTypeName, factoryImplementationName.trim())
spring.factories 对应结构
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
复制代码
可以看到 , factoryTypeName 是第一个节点 , 地下为对应的实现
getResources 和 getSystemResources 的区别
3.3 Environment 流程 加载 loadFactories
加载的起点
加载 Environment 时加载的起点是 SpringApplication , 他的加载主要来自于 SpringListener
C- SpringApplication
M- run(String... args)
- prepareEnvironment(listeners, applicationArguments)
// 再次调用 : listener.environmentPrepared(environment)
C3- ConfigFileApplicationListener
M3_01- loadPostProcessors() : 加载
// M3_01 伪代码
return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
复制代码
加载流程
专门截取出来主要是因为这里调用的方法和上一个是有区别的
loadFactories 除了获取上文的集合 ,同时会通过 instantiateFactory 加载实例 , 他从“META-INF/spring”中加载并实例化给定类型的工厂实现.
C2- SpringFactoriesLoader
M2_03- loadFactories
复制代码
P- FACTORIES_RESOURCE_LOCATION :
-> 静态属性,定义了读取的是 "META-INF/spring.factories" 配置文件
P- Map<ClassLoader, MultiValueMap<String, String>> cache
-> 读取 "META-INF/spring.factories" 配置文件的缓存
M- loadFactories : 获得接口对应的实现类名们,然后创建对应的对象们
-> classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
-> loadFactoryNames(factoryClass, classLoaderToUse);
?- 获得接口对应的实现类名们
-> new ArrayList<>(factoryNames.size());
FOR-> 遍历 factoryNames 数组,创建实现类的对象
-> result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
-> AnnotationAwareOrderComparator.sort(result); -- 排序
M- instantiateFactory
-> ClassUtils.forName(instanceClassName, classLoader);
?- 获得 Class 类
-> if (!factoryClass.isAssignableFrom(instanceClass)) {
?- 判断是否实现了指定接口
-> throw new IllegalArgumentException
-> ReflectionUtils.accessibleConstructor(instanceClass).newInstance();
-> 创建对象
复制代码
其他主要使用 factories 的位置
// 加载 EnvironmentPostProcessor 的 Factories
C- ConfigFileApplicationListener
M- loadPostProcessors
- SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader())
C- ConfigFileApplicationListener
- SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,getClass().getClassLoader());
C- AutoConfigurationImportSelector
- SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
复制代码
附录: 2者对比
PS : 看了一下好像没什么区别 , 方式一创建的大多数是 ApplicationContextInitializer 和 ApplicationListener 的对象 , 从而被设置到 SpringApplication 中
3.4 factories 文件的扫描
核心逻辑在 loadSpringFactories 方法中
C2- SpringFactoriesLoader
M2_02- loadSpringFactories(@Nullable ClassLoader classLoader)
- PropertiesLoaderUtils.loadProperties
C- PropertiesLoaderUtils
M- loadProperties : 调用 fillProperties
M- fillProperties : 后面就是读成一个 inputStream 了 , 没有看的价值
- String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
- String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
- put(key, value);
// PS : Properties extends Hashtable<Object,Object>
复制代码
3.5 使用技巧
TODO : 今天不想写了 , 看心情补
四 . 总结
基础 : SPI 机制 + Properties 扫描能力
流程 : 启动时扫描ApplicationContextInitializer + ApplicationListener , 运行时扫描其他相关Factories , 并且实例化
总结 : Factories 比较简单 ,到最后也没看懂为什么会有2种不同的加载途径 , 其底层都是使用 构造器 + instance 实现一个对象 , 猜测可能和加载顺序优化
PS : 有清楚的欢迎在评论告知一下 感谢❤❤❤