前言 intro
Hello , AV Body! 我是野区杰西。上篇文章我们知道了 SPI 和 Spring Factory 提供了扩展的机制。这次我们讲一下。关于 Spring 是如何实现 Spring Factory 的。
源码解析
还记得,上一篇文章 从 Java SPI 到 Spring Factories 扩展 我们在文末后得出一个结论:
Tips:扩展方式 = 基于接口编程 + 配置文件 + 策略模式
现在我们配置文件是 spring.factories,那究竟是 spring 在哪里读取了这些文件呢?其实我们可以使用 idea 的功能来看下哪个类进行了读取。
首先我们可以使用 spring.factories 作为关键字进行搜寻。window 环境下请使用 ctrl+shitf+R 打开搜寻框;macos 使用 command+房子键+R 打开搜索框。
然后输入 spring.factories 选择检索文件类型 *.java,选择范围(scope) 为 Project And Libraries,如下图:
这时候,我们会搜到一个叫 SpringFactoriesLoader 的 Java 类。(这个名字已经很明显知道这就是加载 spring.factories 的类了)
观察一下这里的代码,我们会发现,有一个 loadSpringFactories() 方法是对读取了其属性 FACTORIES_RESOURCE_LOCATION。我们可以得知,这个应该是进行读取所有 classpath 下 jar 的 spring.factories 文件的方法。那我们可以在这个方法打一下断点,康康 Spring 是怎么调用这个方法的。打了断点,debug 后如下图
在 debug 后的调用链,我们可以发现其实是从 SpringApplication 开始调用的。
public class SpringApplication {
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
}
复制代码
我们可以提取关键两句代码
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
复制代码
这两句分别是负责设置系统的初始化器(Spring Boot 用于初始化系统的参数)和监听器的。我们继续沿着方法跟进去
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 1⃣
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 2⃣
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 3⃣
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
复制代码
两个同名方法,为了使入参进行兼容。终于我们发现,这里是使用了 SpringFactoriesLoader 类,其直接调用了静态方法 loadFactoryNames。
上面我标了顺序,我们知道了第一步其实就是去 loadSpringFactories,也就是读取配置文件。
public class SpringApplication {
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// cache 是一个 ConcurrentReferenceHashMap 作为缓存,以 classloader 作为键
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 若没传 classloader 则使用当前系统的 loader
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();
// 封装成 url source
UrlResource resource = new UrlResource(url);
// 读取成 property 对象
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// 循环 key。还记得之前 spring.factories 里面,以接口为键,以实现类为值
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) {
}
}
}
复制代码
ok,上述代码已经帮我们解析结果放在了 Map 中。现在我们要把这些结果实例化。
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
// 根据 bean name 进行循环实例
for (String name : names) {
try {
// 根据 name 获取 class
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
// 选取构造器
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
// 实例化
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
复制代码
最后一步 AnnotationAwareOrderComparator.sort() ,是通过 @Order 注解来对 SpringFactories 解析的结果进行排序。
conclusion 结论
总结流程:其实这个解析过程虽然蛮简单的,但是却是一个扩展方式很好的体现,以后写简单的框架可以以这种方式预留扩展给开发者。
ok,解析完成!