概述
类名 : SpringFactoriesLoader
所在包 : org.springframework.core.io.support
官方文档
该类并不对外暴露给应用开发者使用,而是Spring
框架自己使用的内部工具类,本身被声明为抽象类(abstract
),不可以被实例化。
在 Spring Boot
应用启动的过程中,这个类的工作很重要, 启动逻辑使用该类从classpath
上所有jar
包中找出各自的 META-INF/spring.factories
属性文件,并分析出其中定义的工厂类。这些工厂类进而被启动逻辑使用,应用于进一步初始化工作。
功能介绍
- 类静态成员常量
final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
此常量定义了该工具类要从每个jar
包中提取的工厂类定义属性文件的相对路径。
- 类静态方法
<T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader)
此方法会读取classpath
上所有的jar
包中的所有 META-INF/spring.factories
属性文件,找出其中定义的匹配类型 factoryClass
的工厂类,然后创建每个工厂类的对象/实例,并返回这些工厂类对象/实例的列表。
- 类静态方法
List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader)
此方法会读取classpath
上所有的jar
包中的所有META-INF/spring.factories
属性文件,找出其中定义的匹配类型 factoryClass
的工厂类,然后并返回这些工厂类的名字列表,注意是包含包名的全限定名。
关于应用
Spring Boot
提供的一些JAR
包,里面会带有文件META-INF/spring.factories
。Spring Boot
应用启动的时候,根据启动阶段不同的需求,框架就会调用SpringFactoriesLoader
加载相应的工厂配置信息。
比如Spring Boot
应用使用了注解@EnableAutoConfiguration
时,就会触发对SpringFactoriesLoader.loadFactoryNames()
的调用。具体请参考 :
Spring EnableAutoConfigurationImportSelector 是如何工作的 ?
还有其他一些应用点,比如 :
// SpringApplication.initialize
// => SpringApplication.getSpringFactoriesInstances()
SpringFactoriesLoader.loadFactoryNames(org.springframework.context.ApplicationContextInitializer)
// SpringApplication.initialize
// => SpringApplication.getSpringFactoriesInstances()
SpringFactoriesLoader.loadFactoryNames(org.springframework.context.ApplicationListenr)
// SpringApplication.run
// => getRunListeners
// => SpringApplication.getSpringFactoriesInstances()
SpringFactoriesLoader.loadFactoryNames(org.springframework.boot.SpringApplicationRunListener)
// SpringApplication.run
// => prepareEnvironment
// => SpringApplication.getSpringFactoriesInstances()
// => ConfigFileApplicationListener.onApplicationEnvironmentPreparedEvent() //事件处理
// => loadPostProcessors()
SpringFactoriesLoader.loadFactoryNames(org.springframework.boot.env.EnvironmentPostProcessor)
当然还有其他一些入口点,这里就不一一列举。
源代码解析
源代码版本 :
springboot 2.1.0
本文源代码基于springboot 2.1.0
,跟之前springboot 1.5.x
版本相比,SpringFactoriesLoader
实现已经有了变化,
比如在新的实现中已经加入了缓存机制。如果相应的文件需要被读取多次的话,第一次读取后会缓存起来,之后
的读取会使用缓存数据。
package org.springframework.core.io.support;
// 省略 import 行
/**
* General purpose factory loading mechanism for internal use within the framework.
* 仅限框架内部使用的工具类,通用目的的工厂加载机制。
*
* SpringFactoriesLoader#loadFactories loads and instantiates
* factories of a given type from #FACTORIES_RESOURCE_LOCATION files which
* may be present in multiple JAR files in the classpath. The spring.factories
* file must be in Properties format, where the key is the fully qualified
* name of the interface or abstract class, and the value is a comma-separated list of
* implementation class names.
*
* SpringFactoriesLoader#loadFactories设计用于加载和实例化指定类型的工厂,这些工厂类型的定义
* 来自classpath中多个JAR包内常量FACTORIES_RESOURCE_LOCATION所指定的那些spring.factories文件。
* spring.factories文件的格式必须是属性文件格式,每条属性的key必须是接口或者抽象类的全限定名,
* 而属性值value是一个逗号分割的实现类的名称。
*
* 举例来讲,一条属性定义如下:
*
* example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
*
* 这里 example.MyService 是接口或者抽象类的全限定名, MyServiceImpl1和MyServiceImpl2是相应的
* 两个实现类。
*
*/
public final class SpringFactoriesLoader {
/**
* The location to look for factories.
* Can be present in multiple JAR files.
* 在classpath中的多个JAR中要扫描的工厂配置文件的在本JAR包中的路径。
* 实际上,Springboot的每个 autoconfigure包都包含一个这样的文件。
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
// 缓存,在 loadFactoryNames 首次被调用时,所有jar包中的 META-INF/spring.factories
// 文件内容都会被加载,然后缓存在 cache 中, 注意 cache Map 的 key 是 loadFactoryNames
// 调用时的参数 classLoader, 而 value 是另外一个 Map,其 key 是工厂类的名称,
// 也就是每个 META-INF/spring.factories 属性文件中属性名部分
private static final Map<ClassLoader, MultiValueMap<String, String>> cache =
new ConcurrentReferenceHashMap<>();
private SpringFactoriesLoader() {
}
/**
* Load and instantiate the factory implementations of the given type from
* #FACTORIES_RESOURCE_LOCATION, using the given class loader.
* The returned factories are sorted through AnnotationAwareOrderComparator.
* If a custom instantiation strategy is required, use #loadFactoryNames
* to obtain all registered factory names.
* @param factoryClass the interface or abstract class representing the factory
* @param classLoader the ClassLoader to use for loading (can be null to use the default)
* @throws IllegalArgumentException if any factory implementation class cannot
* be loaded or if an error occurs while instantiating any factory
* @see #loadFactoryNames
*/
public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryClass, "'factoryClass' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// 加载类型为factoryClass的工厂的名称,其实是一个个的全限定类名,使用指定的classloader:
// classLoaderToUse
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
}
List<T> result = new ArrayList<>(factoryNames.size());
// 实例化所加载的每个工厂类
for (String factoryName : factoryNames) {
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
// 排序
AnnotationAwareOrderComparator.sort(result);
return result;
}
/**
* Load the fully qualified class names of factory implementations of the
* given type from #FACTORIES_RESOURCE_LOCATION, using the given
* class loader.
* @param factoryClass the interface or abstract class representing the factory
* @param classLoader the ClassLoader to use for loading resources; can be
* null to use the default
* @throws IllegalArgumentException if an error occurs while loading factory names
* @see #loadFactories
*/
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
// 1. 使用指定的classloader扫描classpath上所有的JAR包中的文件META-INF/spring.factories,加载其中的多值
// 工厂属性定义,使用多值Map的形式返回,
// 2. 返回多值Map中key为factoryClassName的工厂名称列表,如果没有相应的entry,返回空列表而不是返回null
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
/**
* 使用指定的classloader扫描classpath上所有的JAR包中的文件META-INF/spring.factories,加载其中的多值
* 工厂属性定义,使用多值Map的形式返回
**/
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 扫描classpath上所有JAR中的文件META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
// 找到的每个META-INF/spring.factories文件都是一个Properties文件,将其内容
// 加载到一个 Properties 对象然后处理其中的每个属性
URL url = urls.nextElement();
// url 对应某个 META-INF/spring.factories 配置文件资源
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// properties 来自 url 对应某个 META-INF/spring.factories 配置文件资源
for (Map.Entry<?, ?> entry : properties.entrySet()) {
// 获取工厂类名称(接口或者抽象类的全限定名)
String factoryClassName = ((String) entry.getKey()).trim();
// 将逗号分割的属性值逐个取出,然后放到多值Map结果result中去。
for (String factoryName : StringUtils.commaDelimitedListToStringArray(
(String) entry.getValue())) {
// 放到 result 中 :
// key 使用 factoryClassName
// value 可能有多值, 使用 factoryName
result.add(factoryClassName, factoryName.trim());
}
}
}
// 放到缓存中,key 使用 classLoader
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
/**
* @param instanceClassName 工厂实现类全限定名称
* @param factoryClass 工厂所属接口/抽象类全限定名称
* @param classLoader 所要使用的类加载器
**/
@SuppressWarnings("unchecked")
private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass,
ClassLoader classLoader) {
try {
Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
if (!factoryClass.isAssignableFrom(instanceClass)) {
throw new IllegalArgumentException(
"Class [" + instanceClassName + "] is not assignable to ["
+ factoryClass.getName() + "]");
}
return (T) ReflectionUtils.accessibleConstructor(instanceClass).newInstance();
}
catch (Throwable ex) {
throw new IllegalArgumentException("Unable to instantiate factory class: "
+ factoryClass.getName(), ex);
}
}
}