Spring Factories机制
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机制相关推荐
- spring.factories机制
spring.factories 在Spring中有一种非常解耦的扩展机制:Spring Factories.这种扩展机制实际上是仿照Java中的SPI扩展机制来实现的. Java SPI约定 Jav ...
- springboot核心基础之spring.factories机制
引言 在java spring cloud项目中,我们常常会在子模块中创建公共方法,那么在另外一个子模块中,需要加载配置文件的时候,往往Spring Boot 自动扫描包的时候,只会扫描自己模块下的类 ...
- 【方向盘】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工程可公开访问啦 ...
- Spring的Factories机制介绍
在看本节文章之前,建议大家先去了一下java的SPI机制,因为Spring的Factories就是Spring版本的Java Spi,我在关于java基础系列文章中有详细介绍Java SPI机制. S ...
- SpringBoot解耦的扩展机制 Spring Factories介绍及使用
一.什么是 SPI机制 Spring Boot中有一种非常解耦的扩展机制:Spring Factories.这种扩展机制实际上是仿照Java中的SPI扩展机制来实现的.SPI的全名为Service P ...
- SpringBoot扩展机制——spring factories
介绍 Spring Boot中有一种非常解耦的扩展机制:Spring Factories.这种扩展机制实际上是仿照 Java 中的 SPI 扩展机制来实现的.它在META-INF/spring.fac ...
- JDK的 SPI 机制,Spring的 factories 机制
目录 jdk的SPI机制 spi简介 使用示例 spring的factories机制 factories机制简介 使用示例 总结 jdk的SPI机制 spi简介 模块之间一般推荐基于接口编程,不与具体 ...
- Spring Factories及 SPI机制
Spring Boot中有一种非常解耦的扩展机制:Spring Factories.这种扩展机制实际上是仿照Java中的SPI扩展机制来实现的. springboot中SPI机制 java中的SPI机 ...
- spring启动加载机制spring.factories使用方法
步骤1:在resources/META-INF目录下创建名为spring.factories的文件 步骤2:内容以key=value的方式配置需要加载的类型,如下: key为接口类,可以使用sprin ...
最新文章
- 智慧城市 android,基于Android系统的智慧城市服务客户端的设计与实现
- 没有人能够解释为什么飞机可以待在空中
- 【分布式】Zookeeper请求处理
- Cobbler体验小记
- iOS中的UIScrollView(滑动视图)
- 图像数集据增广的15+种功能总结和Python代码实现
- 无人车运动参数校准流程
- SqlHelper的封装
- pandox怎么用_神器Pandoc的安装与使用 | Flyaway's Blog
- minio服务器在win10的上传与下载,以及修改头像Minio速看免费本地文件服务器
- [高数][高昆轮][高等数学上][第一章-函数与极限]03.函数的极限
- 插入栈顶元素c语言,详解数据结构之顺序栈的基本操做(C语言描述)
- Android消息机制(Handler机制) - 线程的等待和唤醒
- 时空同步图卷积网络:时空网络数据预测的新框架
- Attribute name aphmodel associated with an element type mxg must be followed by the ' = ' charac
- 计算机专业英文简历和求职信,毕业生通用英文简历范文和求职信.doc
- 一起来看流星雨剧情简介/剧情介绍/剧情分集介绍第十集
- 行星图(planet map)
- GLONASS与GPS时间统一
- FlowNet3D - Learning Scene Flow in 3D Point Clouds[CVPR2019]