SpringFactoriesLoader源码分析
前言
本文主要记录 SpringFactoriesLoader 里的一些方法的源码,供自己学习记录。
一、SpringFactoriesLoader是什么?
SpringFactoriesLoader 工厂加载机制是 Spring 内部提供的一个加载方式,只需要在模块的 META-INF/spring.factories 文件中,以 Properties 类型(即 key-value 形式)配置,就可以将相应的实现类注入 Spirng 容器中。示例如下:
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
这里需要注意的是 SpringFactoriesLoader 是 final
类型,里面对外提供的方法为静态方法,可以直接使用。
二、方法解析
1.loadFactories
加载我们指定的类下需要导入的类,实例化返回。
public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
// factoryClass 也就是我们所说的 key
Assert.notNull(factoryClass, "'factoryClass' must not be null");
// 获取类加载器
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// 获取需要加载的类的名称
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));
}
// 按照 order 进行排序
AnnotationAwareOrderComparator.sort(result);
return result;
}
2.loadFactoryNames
获取需要加载类的全限定名,并返回。
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
// 获取 key 的值
String factoryClassName = factoryClass.getName();
// loadSpringFactories 获取 classLoader 加载器能够加载的所有类,这里是返回的 map
// 如果说有我们指定的 key 则返回对应结果,如果不包含则返回 空集合
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>()
// cache 用于缓存一次扫描的结果
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 这个过程就是 寻找我们项目里所有 jar 包下的 META-INF/spring.factories 文件
// 将其进行解析,并放置到缓存中
// 这样做的好处在于,一次load获取所有结果,而加载的调用过程是分布进行的
// 加载一次之后,就可以通过缓存获取,非常方便
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 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);
}
}
3.instantiateFactory
instantiateFactory 就是实例化的过程。
private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
try {
// 通过反射机制实现动态加载
Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
// 判断 要实例化的对象不是继承于 factoryClass 也就是 key
// eg: SpringApplicationRunListener 要加载 EventPublishingRunListener
// 这里 EventPublishingRunListener 是继承 SpringApplicationRunListener
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);
}
}
总结
SpringFactoriesLoader 在SpringBoot项目启动时使用很多,可以结合分析 SpringApplication 的 run
方法一起分析。有兴趣的话也可以同步分析一下读取 META-INF/spring.factories 文件的过程~