Spring Factories机制简述

Spring Factories机制和Java SPI的扩展机制类似,Spring Boot采用了spring.factories的扩展机制,在很多spring的starter 包中都可以看到,通过读取 META-INF/spring.factories文件中的配置指定自动配置类入口,然后在程序中读取这些配置文件并实例化,从而让框架加载该类实现jar的动态加载。比如我们自定义的一些Spring Boot Starter公共组件就可以使用Spring Factories机制,通过极简的配置就可以在需要使用组件的地方引入依赖直接使用。

什么是SPI机制

SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。

这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制。

Spring Factories实现原理

spring-core包(博主的是版本是***5.2.8.RELEASE***)里定义了SpringFactoriesLoader类,这个类实现了检索META-INF/spring.factories文件,并获取指定接口的配置的功能。

以下为SpringFactoriesLoader类的注释说明:

General purpose factory loading mechanism for internal use within the framework
即该类是为框架内部使用工厂加载机制服务的。

SpringFactoriesLoader loads and instantiates factories of a given type from FACTORIES_RESOURCE_LOCATION files which may be present in multiple JAR files in the classpath.
SpringFactoriesLoader类通过读取spring.factories文档(该文档会存在多个classpath的多个jar包中),对给定的type及factoryClass加载和实例化其工厂类。

The spring.factories file must be in java.util.Properties format, where the key is the fully qualified name of the interface or abstract class, and the value is a comma-separated list of implementation class names.
spring.factories格式:key为接口或抽象类全称,value为具体实现类全称的列表

举例如下:

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

通过FACTORIES_RESOURCE_LOCATION指定扫描的配置文件路径:

/*** The location to look for factories.* <p>Can be present in multiple JAR files.*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

类中定义了两个公共的方法:

  • loadFactories 根据接口类获取其实现类的实例,这个方法返回的是对象列表。
  • loadFactoryNames 根据接口获取其接口类的名称,这个方法返回的是类名的列表。

其中还包含了私有方法:

  • instantiateFactory 根据类名创建实例对象。
  • loadSpringFactories 通过扫描配置文件获取接口名称和接口实现类名称列表。

loadFactories

loadFactories方法首先调用loadFactoryNames方法获取待实例化的具体实现类的全称,然后调用instantiateFactory方法实例化每一个具体实现类,最终返回一个具体实现类的实例列表。

public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {Assert.notNull(factoryType, "'factoryType' must not be null");ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}//调用loadFactoryNames获取接口的实现类List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);if (logger.isTraceEnabled()) {logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);}//遍历 factoryNames 数组,创建实现类的对象List<T> result = new ArrayList<>(factoryImplementationNames.size());for (String factoryImplementationName : factoryImplementationNames) {//调用instantiateFactory根据类创建实例对象result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));}//排序AnnotationAwareOrderComparator.sort(result);return result;
}

instantiateFactory

根据实现类名实例化每一个具体实现类返回

@SuppressWarnings("unchecked")
private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {try {Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);if (!factoryType.isAssignableFrom(factoryImplementationClass)) {throw new IllegalArgumentException("Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");}return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();}catch (Throwable ex) {throw new IllegalArgumentException("Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",ex);}
}

loadFactoryNames

在这个方法中会遍历整个ClassLoader中所有jar包下的spring.factories文件。通过Properties解析所有接口的实现类名称。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

loadSpringFactories

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);Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();//StringUtils.commaDelimitedListToStringArray:将配置文件中的value(接口实现类名称)通过","分隔成String数组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);}
}

Spring Factories机制相关推荐

  1. spring.factories机制

    spring.factories 在Spring中有一种非常解耦的扩展机制:Spring Factories.这种扩展机制实际上是仿照Java中的SPI扩展机制来实现的. Java SPI约定 Jav ...

  2. springboot核心基础之spring.factories机制

    引言 在java spring cloud项目中,我们常常会在子模块中创建公共方法,那么在另外一个子模块中,需要加载配置文件的时候,往往Spring Boot 自动扫描包的时候,只会扫描自己模块下的类 ...

  3. 【方向盘】Spring Boot 2.7.0正式发布,弃用OkHttp 3、弃用spring.factories机制

    Spring Boot 2.7.0.2.6.8.25.14发布,2.5.x停止OSS支持. 本文已被https://yourbatman.cn收录:女娲Knife-Initializr工程可公开访问啦 ...

  4. Spring的Factories机制介绍

    在看本节文章之前,建议大家先去了一下java的SPI机制,因为Spring的Factories就是Spring版本的Java Spi,我在关于java基础系列文章中有详细介绍Java SPI机制. S ...

  5. SpringBoot解耦的扩展机制 Spring Factories介绍及使用

    一.什么是 SPI机制 Spring Boot中有一种非常解耦的扩展机制:Spring Factories.这种扩展机制实际上是仿照Java中的SPI扩展机制来实现的.SPI的全名为Service P ...

  6. SpringBoot扩展机制——spring factories

    介绍 Spring Boot中有一种非常解耦的扩展机制:Spring Factories.这种扩展机制实际上是仿照 Java 中的 SPI 扩展机制来实现的.它在META-INF/spring.fac ...

  7. JDK的 SPI 机制,Spring的 factories 机制

    目录 jdk的SPI机制 spi简介 使用示例 spring的factories机制 factories机制简介 使用示例 总结 jdk的SPI机制 spi简介 模块之间一般推荐基于接口编程,不与具体 ...

  8. Spring Factories及 SPI机制

    Spring Boot中有一种非常解耦的扩展机制:Spring Factories.这种扩展机制实际上是仿照Java中的SPI扩展机制来实现的. springboot中SPI机制 java中的SPI机 ...

  9. spring启动加载机制spring.factories使用方法

    步骤1:在resources/META-INF目录下创建名为spring.factories的文件 步骤2:内容以key=value的方式配置需要加载的类型,如下: key为接口类,可以使用sprin ...

最新文章

  1. 智慧城市 android,基于Android系统的智慧城市服务客户端的设计与实现
  2. 没有人能够解释为什么飞机可以待在空中
  3. 【分布式】Zookeeper请求处理
  4. Cobbler体验小记
  5. iOS中的UIScrollView(滑动视图)
  6. 图像数集据增广的15+种功能总结和Python代码实现
  7. 无人车运动参数校准流程
  8. SqlHelper的封装
  9. pandox怎么用_神器Pandoc的安装与使用 | Flyaway's Blog
  10. minio服务器在win10的上传与下载,以及修改头像Minio速看免费本地文件服务器
  11. [高数][高昆轮][高等数学上][第一章-函数与极限]03.函数的极限
  12. 插入栈顶元素c语言,详解数据结构之顺序栈的基本操做(C语言描述)
  13. Android消息机制(Handler机制) - 线程的等待和唤醒
  14. 时空同步图卷积网络:时空网络数据预测的新框架
  15. Attribute name aphmodel associated with an element type mxg must be followed by the ' = ' charac
  16. 计算机专业英文简历和求职信,毕业生通用英文简历范文和求职信.doc
  17. 一起来看流星雨剧情简介/剧情介绍/剧情分集介绍第十集
  18. 行星图(planet map)
  19. GLONASS与GPS时间统一
  20. FlowNet3D - Learning Scene Flow in 3D Point Clouds[CVPR2019]

热门文章

  1. 不愧是华为研究院19级研究员,轻松就把网络协议趣谈了
  2. openshift+SkyDive 监控---离线安装
  3. 阿里云对象存储OSS服务开通
  4. 使用命令行,下载网络m3u8文件为mp4视频,保存m3u视频
  5. [线段树]打字练习2
  6. SuperScan扫描端口
  7. c语言 求特殊方程的正整数解
  8. linux的RHCS服务集群之Heartbeat集群简单搭建
  9. c语言实现按键的抖动与消除,8051单片机实验2——按键识别(一)
  10. 82年码农研发经理被裁,人到中年不如狗!