盘点 SpringBoot Factories 处理流程

首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜

文章合集 : 🎁 juejin.cn/post/694164…

Github : 👉 github.com/black-ant

CASE 备份 : 👉 gitee.com/antblack/ca…

一 . 前言

之前说了 SpringBoot 自动配置的地方 , 其中就涉及到 Factories 的加载 , 这一篇详细的把这个流程过一下.

Factories 的作用是工厂类的元数据 , Spring 会通过 .factories 文件将需要初始化的类反射出来

二 . 核心类

Factories 流程中核心的类是 SpringFactoriesLoader , 在 SpringApplication run 时 , 即会加载 Factories相关属性

  • spring.factories 配置文件 : Spring 自己的一套 SPI 机制的配置文件
  • SpringFactoriesLoader 类,用于加载 spring.factories 配置文件

先看一下 SpringFactoriesLoader 主要流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
java复制代码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 中.

1
2
3
4
5
6
7
8
9
10
java复制代码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 加载流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
java复制代码
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())

image.png

spring.factories 对应结构

1
2
3
4
5
6
7
8
9
10
11
properties复制代码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

1
2
3
4
5
6
7
8
9
10
11
12
java复制代码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”中加载并实例化给定类型的工厂实现.

1
2
3
java复制代码
C2- SpringFactoriesLoader
M2_03- loadFactories
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
java复制代码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 的位置

1
2
3
4
5
6
7
8
9
10
11
12
java复制代码
// 加载 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 中

image.png

3.4 factories 文件的扫描

核心逻辑在 loadSpringFactories 方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码
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 : 有清楚的欢迎在评论告知一下 感谢❤❤❤

更新记录

  • V20210804 : 优化布局

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%