源码级深度理解 Java SPI( 七 )


4.3.5 SpringFactoriesLoader.loadFactoryNames 方法@Import(AutoConfigurationImportSelector.class) 表示直接注入AutoConfigurationImportSelector 。
AutoConfigurationImportSelector 有一个核心方法getCandidateConfigurations 用于获取候选配置 。该方法调用了SpringFactoriesLoader.loadFactoryNames 方法,这个方法即为 Spring Boot SPI 的关键,它负责加载所有 META-INF/spring.factories 文件,加载的过程由 SpringFactoriesLoader 负责 。
Spring Boot 的 META-INF/spring.factories 文件本质上就是一个 properties 文件,数据内容就是一个个键值对 。
SpringFactoriesLoader.loadFactoryNames 方法的关键源码:
// spring.factories 文件的格式为:key=value1,value2,value3// 遍历所有 META-INF/spring.factories 文件// 解析文件,获得 key=factoryClass 的类名称public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {// 尝试获取缓存,如果缓存中有数据,直接返回MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {// 获取资源文件路径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);// 解析文件,得到对应的一组 PropertiesProperties properties = PropertiesLoaderUtils.loadProperties(resource);// 遍历解析出的 properties,组装数据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);}}归纳上面的方法,主要作了这些事:
加载所有 META-INF/spring.factories 文件,加载过程有 SpringFactoriesLoader 负责 。

  • 在 CLASSPATH 中搜寻所有 META-INF/spring.factories 配置文件 。
  • 然后,解析 spring.factories 文件,获取指定自动装配类的全限定名 。
4.3.6 Spring Boot 的 AutoConfiguration 类Spring Boot 有各种 starter 包,可以根据实际项目需要,按需取材 。在项目开发中,只要将 starter 包引入,我们就可以用很少的配置,甚至什么都不配置,即可获取相关的能力 。通过前面的 Spring Boot SPI 流程,只完成了自动装配工作的一半,剩下的工作如何处理呢 ?
以 spring-boot-starter-web 的 jar 包为例,查看其 maven pom,可以看到,它依赖于 spring-boot-starter,所有 Spring Boot 官方 starter 包都会依赖于这个 jar 包 。而 spring-boot-starter 又依赖于 spring-boot-autoconfigure,Spring Boot 的自动装配秘密,就在于这个 jar 包 。
从 spring-boot-autoconfigure 包的结构来看,它有一个 META-INF/spring.factories ,显然利用了 Spring Boot SPI,来自动装配其中的配置类 。
源码级深度理解 Java SPI

文章插图
下图是 spring-boot-autoconfigure 的 META-INF/spring.factories 文件的部分内容,可以看到其中注册了一长串会被自动加载的 AutoConfiguration 类 。
源码级深度理解 Java SPI

文章插图
以 RedisAutoConfiguration 为例,这个配置类中,会根据 @ConditionalXXX 中的条件去决定是否实例化对应的 Bean,实例化 Bean 所依赖的重要参数则通过 RedisProperties 传入 。

经验总结扩展阅读