SpringFactoriesLoader

大致介绍

SpringFactoriesLoader是 spring 的一个核心类,在 spring-core.jar 包中。它的作用是,用ClassLoader找出项目依赖jar包中的所有“META-INF/spring.factories”文件,将里面的内容加载成 Properties对象(key value对,一个文件加载出一个 Properties),再由 Properties 对象转为 Map<String, List> 格式。

以下面这个文件为例:

1
2
3
4
5
6
7
8
9
10
11
12
spring-boot-autoconfigure-2.3.0.RELEASE.jar META-INF/spring.factories

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

。。。。。

这个文件加载出的 Properties 对象内容有:

1
2
3
4
5
6
7
key1 = "org.springframework.context.ApplicationContextInitializer"
value1 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"

key2 = "org.springframework.context.ApplicationListener"
value2 = "org.springframework.boot.autoconfigure.BackgroundPreinitializer"

等等

最后转成的 Map<String, List> 格式内容为:

1
2
3
4
5
6
7
8
key1 = "org.springframework.context.ApplicationContextInitializer"
value1List = ["org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer","org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"]
-- 这个 value1List 其实就是把 Properties 对象的 value 保存的字符串按逗号分隔变成的数组

key2 = "org.springframework.context.ApplicationListener"
value2List = ["org.springframework.boot.autoconfigure.BackgroundPreinitializer"]

等等

扫描这个“META-INF/spring.factories”文件的目的是什么?
SpringBoot 默认会扫描并管理在启动类包路径下的 Bean。如果有想要被扫描但不想改变默认扫描的路径,比如自己写的 jar 包中有 Bean,但不要求使用这个 jar 的应用必须修改默认扫描路径,就在自己 jar 中添加这个“META-INF/spring.factories”文件,将 jar 中的 Bean 的接口与实现类写上,SpringBoot 启动时会在某个阶段?将这些Bean扫描并管理。

在哪些路径下寻找spring.factories文件

SpringFactoriesLoader.loadSpringFactories方法中的下面这段代码:

1
2
3
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

其中 classLoader,即类加载器,是这个方法的入参,可以为null,为null时使用“系统类加载器”,即“应用程序类加载器”,常量 FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”。

这段代码通过调用classLoader.getResources方法返回一个CompoundEnumeration<URL>对象,元素URL中保存的就是某一个“spring.factories”文件所在的绝对路径,如“file:/D:/Development/maven-repo/org/springframework/boot/spring-boot/2.3.0.RELEASE/spring-boot-2.3.0.RELEASE.jar!/META-INF/spring.factories”。但是,在这行代码中还不会得到“spring.factories”文件所在的绝对路径,可以认为这时返回的CompoundEnumeration<URL>对象中的URL元素是空的,它只是获取出会在哪些路径下寻找 URL。

会在哪些路径下寻找 URL 呢?研究ClassLoader.getResources源码就会知道,这些路径就是 启动类加载器+扩展类加载器+应用程序类加载器 3个类加载器负责的路径的并集,即”JVM系统参数sun.boot.class.path” + “JVM系统参数java.ext.dirs” + “环境变量classpath” 包含的路径的并集!也就是说,SpringFactoriesLoader 将会去启动类加载器、扩展类加载器、应用程序类加载器它们3个负责加载的全部路径中,寻找“META-INF/spring.factories”文件,只不过,这个文件一般只存在于应用程序类加载器负责的路径,即“环境变量classpath”指定的路径下。

上面说到,从classLoader.getResources方法返回的变量中还没有找到“spring.factories”文件所在的绝对路径,每个文件的绝对路径是在后面的代码中获取的(代码解释见注释):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 。。。
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) { // 在这一步查找具体的路径,遍历所有路径,从一个路径下找到了就返回true,下一次循环就从已找到路径的下一个开始遍历,找到就返回,最后遍历完所有路径返回false
URL url = urls.nextElement(); // 取出这一次循环找到的路径
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource); // 文件内容转为Properties对象
for (Map.Entry<?, ?> entry : properties.entrySet()) { // Properties对象中key-value转为Map<String, List<String>> result格式
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;
}
// 。。。
}

loadFactoryNames 方法

已知loadSpringFactories方法返回的 Map<String, List> result 中,key为接口全限定名,value为“spring.factories”文件中配置的该接口的实现类全限定名。

loadFactoryNames方法是根据入参Class<?> factoryType指定的 className(接口名),从 loadSpringFactories 方法返回的 Map 中取该接口名对应的实现类名 List。

代码最后的 getOrDefault 意思是如果返回的 Map 中没有指定 key 对应的 value,那么就返回 value=空List,如果有就返回真正的 value。

1
2
3
4
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

loadFactories 方法

这个方法首先通过loadFactoryNames方法获取指定接口的实现类名 List(变量 factoryImplementationNames),再用instantiateFactory方法根据这些实现类名反射创建这些实现类的实例对象,如果这些实现类有使用PriorityOrderedOrdered,还会根据其标注的顺序排序,最后返回排序后的实现类实例对象 List。

排序方法 AnnotationAwareOrderComparator :

  1. 用于比较的两个对象都实现了 PriorityOrdered 接口,对比它们的 getOrder() 的值,值小的在前
  2. 两个对象中只有一个实现了 PriorityOrdered 接口,则实现了这个接口的排在前
  3. 两个对象中分别查找它们是否实现了Ordered 接口,是则获取 getOrder() 值;若未实现Ordered 接口,就查看它们是否标注了 Order 注解,是则获取注解中的排序值;若也没有 Order 注解,再看是否有 Priority 注解,有则获取注解中的排序值;都没有则返回一个大值,则这个类会被排在后面。排序值小的排在前
  4. 当两个对象排序值相同,会按对象名称排序
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
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryType, "'factoryType' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
}
List<T> result = new ArrayList<>(factoryImplementationNames.size());
for (String factoryImplementationName : factoryImplementationNames) {
result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}

private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
try {
Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
throw new IllegalArgumentException(
"Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
}
return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
ex);
}
}