上一讲【springboot源码解读系列】(四、springboot启动之SpringApplicationRunListeners:通知所有监听器,系统启动中…用于非常早的初始化)说到通过SpringFactoriesLoader加载META-INF/spring.factories获取我们的自定义监听器和启动器。
那么这讲就来揭开SpringFactoriesLoader神秘的面纱:讲解都在注释里面:
值得学习的是他的这种设计理念,通过指定规范,然后对其使用者提供扩展,springboot的启动器原理也是这样来进行实现的。
精华提取:ConcurrentMap(线程安全的map集合)作为缓存cache、通过构造器去创建实例(accessibleConstructor)、isAssignableFrom判断是否是其子类(isAssignableFrom和instanceof关键字的区别)
package org.springframework.core.io.support;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.UrlResource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* 通用工厂加载机制实现的内部使用框架。spring内部使用的
*
* springfactoresloader会加载META-INF/spring.factories文件中给定类型的工厂,
* 为什么是工厂,因为一个key可以对应多个value,
* spring.factories的文件类型是properties格式的,其中键是完全限定的接口或抽象类的名称,值是以逗号分隔的
*
* 比如:
* example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
*
*/
public final class SpringFactoriesLoader {
/**
* 本地的工厂文件,
* 可以存在于多个jar中,也就是他会扫码所有的jar,然后解析所有的META-INF/spring.factories文件,
* 并将其配置的创建其工厂,以及对所有value进行实例化
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// 日志框架
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
// 定义一个map,其中key为ClassLoader,value为MultiValueMap
// MultiValueMap集成自Map
// ConcurrentReferenceHashMap集成自ConcurrentMap,也就是一个线程安全的map
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
// 空参构造函数
private SpringFactoriesLoader() {
}
/**
* 通过classLoader从各个jar包的classpath下面的META-INF/spring.factories加载并解析其key-value值,然后创建其给定类型的工厂实现
*
* 返回的工厂通过AnnotationAwareOrderComparator进行排序过的。
* AnnotationAwareOrderComparator就是通过@Order注解上面的值进行排序的,值越高,则排的越靠后
*
* 如果需要自定义实例化策略,请使用loadFactoryNames方法获取所有注册工厂名称。
*
* @param factoryType 接口或者抽象类的Class对象
* @param classLoader 用于加载的类加载器(可以是null,如果是null,则使用默认值)
* @throws IllegalArgumentException 如果无法加载任何工厂实现类,或者在实例化任何工厂时发生错误,则会抛出IllegalArgumentException异常
*/
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
// 首先断言,传入的接口或者抽象类的Class对象不能为空
Assert.notNull(factoryType, "'factoryType' must not be null");
// 将传入的classLoader赋值给classLoaderToUse
// 判断classLoaderToUse是否为空,如果为空,则使用默认的SpringFactoriesLoader的classLoader
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// 加载所有的META-INF/spring.factories并解析,获取其配置的factoryNames
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;
}
/**
* 使用给定的类加载器从META-INF/spring.factories加载给定类型的工厂实现的完全限定类名。
* @param factoryType 接口或者抽象类的Class对象
*
* @param classLoader classLoader 用于加载的类加载器(可以是null,如果是null,则使用默认值)
*
* @throws IllegalArgumentException 如果在加载工厂名称时发生错误
* @see #loadFactories
*/
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// 通过Class对象获取全限定类名
String factoryTypeName = factoryType.getName();
// loadSpringFactories方法是获取所有的springFactories
// getOrDefault是通过key,获取到对应的类的集合。因为value是通过逗号相隔的,可以有多个,所以是list
// getOrDefault如果存在就返回,如果不存在那么就返回给的默认值
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
// 加载所有的springFactories
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 首先从cache中获取,根据classLoader,
// cache是以ClassLoader作为key的。是静态的final修饰的。整个应用只有一份
MultiValueMap<String, String> result = cache.get(classLoader);
// 如果为null,证明还没有加载过,如果不为空,那么就添加。
if (result != null) {
return result;
}
try {
// 三目表达式,判断参数classLoader是否为空,如果不为空,那么直接使用传入的classLoader获取META-INF/spring.factories
// 如果为空,那么就使用系统的classLoader来获取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
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 解析properties
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// 将所有的key放入result
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;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
// 实例化工厂,根据
@SuppressWarnings("unchecked")
private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
try {
// 根据全限定类名通过反射获取Class对象
Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
// 判断获取的Class对象是否从factoryType里面来,
// 说具体点就是判断我们配置的spring.factories中的权限定类名所对应的类是否是对应的子类或者实现类
// 比如
// org.springframework.context.ApplicationContextInitializer=\
// org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
// org.springframework.boot.context.ContextIdApplicationContextInitializer,
// 那么他就会验证ConfigurationWarningsApplicationContextInitializer和ContextIdApplicationContextInitializer是否是ApplicationContextInitializer的子类
// isAssignableFrom()方法与instanceof关键字的区别总结为以下两个点:
// isAssignableFrom()方法是从类继承的角度去判断,instanceof关键字是从实例继承的角度去判断。
// isAssignableFrom()方法是判断是否为某个类的父类,instanceof关键字是判断是否某个类的子类。
// 如果不是,则会保存
if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
throw new IllegalArgumentException(
"Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
}
// 通过反射的有参构造函数进行实例化:如果直接newInstance的话,那么只能通过空参构造函数进行实例化。
// 通过这种方式可以通过不同参数的构造函数进行创建实例,但是这里并没有传入参数,所以调用的是默认空惨构造函数
return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
ex);
}
}
}