[Spring Boot 源解系列] Spring Factory 源码解析原理

1,499 阅读3分钟

前言 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,解析完成!