Spring Boot 回顾(四):深入理解SpringFactoriesLoader

语言: CN / TW / HK

highlight: a11y-dark theme: smartblue


这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

前言

上一篇我们讲到Spring Boot自动装配的原理,而且通过阅读源码,我们看到其实自动装配的奥义其实就是SpringFactoriesLoader,今天我们再来揭秘下SpringFactoriesLoader的神秘面纱。

介绍

SpringFactoriesLoader工厂的加载机制类似java提供的SPI机制一样,是Spring提供的一种加载方式。只需要在classpath路径下新建一个文件META-INF/spring.factories,并在里面按照Properties格式填写好接口和实现类即可通过SpringFactoriesLoader来实例化相应的Bean。其中key可以是接口、注解、或者抽象类的全名。value为相应的实现类,当存在多个实现类时,用“,”进行分割。其实关于spring.factories的实现也是Spring Boot中约定优于配置的体现,可以说,整个Spring Boot是遵循约定优于配置这个理念产生的,因此它才能如此的快而且简单。

原理

先放一张SpringFactoriesLoader工作的流程图

mermaid graph LR A[Start] -->B{查找缓存}--> |不存在| 1[读取指定路径下的资源文件]-->C[构建Properties对象] -->D[获取指定key对应的value值]-->E[逗号分割value值]-->F[保存结果到缓存]-->2[依次实例化对象]-->3[对结果进行排序]-->4[返回结果]-->5[End] B{查找缓存}--> |存在| 2[依次实例化对象] 接下来我们进入重点,打开SpringFactoriesLoader的源码,里面比较简单,有2个成员变量和4个方法,其中方法分别是loadFactoriesloadFactoryNamesloadSpringFactoriesinstantiateFactory。我们先看下成员变量 ```java

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static final Map> cache = new ConcurrentReferenceHashMap<>(); 其中第一个是寻找工厂的位置,工厂可以存放在多个jar文件中,第二个则是自定义用于存储工厂的缓存。然后我们了解下其中的方法,首先是`loadFactories`这个方法。java public static List loadFactories(Class factoryType, @Nullable ClassLoader classLoader) { Assert.notNull(factoryType, "'factoryType' must not be null"); // 如果未指定类加载器,则使用默认的 ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } // 获取指定工厂名称列表 List factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse); // 如果记录器Trace跟踪激活的话,将工厂名称列表输出 if (logger.isTraceEnabled()) { logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames); } // 创建结果集 List result = new ArrayList<>(factoryImplementationNames.size()); for (String factoryImplementationName : factoryImplementationNames) { // 实例化工厂类,并添加到结果集中 result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse)); } // 对结果集列表进行排序 AnnotationAwareOrderComparator.sort(result); return result; } `` 可以看到大致的工作流程如下: 1. 通过classLoader去加载工厂获取其对应类名称,如果未指定类加载器,则使用默认的; 2. 通过instantiateFactory方法实例化工厂类,并添加到结果集中; 3. 通过AnnotationAwareOrderComparator#sort方法对工厂进行排序; 接着我们看下loadFactoryNames,这个比较简单,主要逻辑都在loadSpringFactories,源码就不放上来了,其逻辑大致是读取 classpath上 所有的 jar 包中的所有 META-INF/spring.factories属 性文件,找出其中定义的匹配类型 factoryClass 的工厂类,然后并返回这些工厂类的名字列表,注意是包含包名的全限定名。最后是instantiateFactory`方法,它的主要作用就是实例化Bean对象

总结

以上我们通过阅读了SpringFactoriesLoader的相关代码加深了对其理解,总结起来就是SpringFactoriesLoader是用于Spring框架内部的通用工厂加载机制。SpringFactoriesLoader通过loadFactories方法来加载并实例化来自FACTORIES_RESOUCE_LOCATION路径中的文件给定的工厂类型,而这些文件可能包含在类路径的jar包中。这些文件通常都命名为spring.factories,并且都是以properties属性作为格式,文件中key表示的是接口或者抽象类的全限定名称,而值是以逗号分隔的实现类的名称列表。