spring.factories是什么?有什么用处?
Posted 流楚丶格念
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring.factories是什么?有什么用处?相关的知识,希望对你有一定的参考价值。
文章目录
作用总结
在看一些开源项目时,我们常常会看到spring.factories 文件,或者读一些Spring-Boot 相关源码时也会遇到,从网上查了查,这个的作用是记录所有需要自动装配进 Spring 容器 bean类,如果你想要实现自己的自动配置,就将你的类通过键值对的方式写在你的spring.factories即可。
问题引出
这里我就产生了一个疑问:
我在自动配置的类已经打上了 @Configuration 的注解不就行了,为什么还要写 spring.factories 文件?
没办法,看来是有必要详细学一下了:
去SpringBoot中探究
在SpringBoot中使用@ComponentScan注解来扫描 @SpringBootApplication 所在的 Application 类所在的包(basepackage)下所有的 @component 注解(或拓展了 @component 的注解)标记的 bean,并注册到 spring 容器中
用过 Spring Boot 的都知道
@ComponentScan 注解的作用是扫描 @SpringBootApplication 所在的 Application 类所在的包(basepackage)下所有的 @component 注解(或拓展了 @component 的注解)标记的 bean,并注册到 spring 容器中。
那么问题来了
在 Spring Boot 项目中,如果你想要被 Spring 容器管理的 bean 不在 Spring Boot 包扫描路径下,怎么办?
解决 Spring Boot 中不能被默认路径扫描的配置类的方式,有 2 种:
(1)在 Spring Boot 主类上使用 @Import 注解
(2)使用 spring.factories 文件
在 Spring 中也有一种类似与 Java SPI 的加载机制。它在 resources/META-INF/spring.factories 文件中配置接口的实现类名称,然后在程序中读取这些配置文件并实例化。
这种自定义的SPI机制是 Spring Boot Starter 实现的基础。
Spring Factories 实现原理是什么?
spring-core 包里定义了 SpringFactoriesLoader 类,这个类实现了检索 META-INF/spring.factories 文件,并获取指定接口的配置的功能。在这个类中定义了两个对外的方法:
loadFactories 根据接口类获取其实现类的实例,这个方法返回的是对象列表。
/**
* 加载并实例化给定类型的工厂实现
* @value #FACTORIES_RESOURCE_LOCATION,使用给定的类加载器。
* 返回的工厂通过 @link AnnotationAwareOrderComparator 排序。
* 如果需要自定义实例化策略,请使用 @link #loadFactoryNames
* 获取所有注册的工厂名称。
*
* @param factoryType 代表工厂的接口或抽象类
* @param classLoader 用于加载的类加载器(可以是 @code null 使用默认值)
* @throws IllegalArgumentException 如果任何工厂实现类不能
* 被加载或在实例化任何工厂时发生错误
* @see #loadFactoryNames
*/
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();
List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
if (logger.isTraceEnabled())
logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
List<T> result = new ArrayList<>(factoryImplementationNames.size());
for (String factoryImplementationName : factoryImplementationNames)
result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
AnnotationAwareOrderComparator.sort(result);
return result;
loadFactoryNames 根据接口获取其接口类的名称,这个方法返回的是类名的列表。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
上面的两个方法的关键都是从指定的 ClassLoader 中获取 spring.factories 文件,并解析得到类名列表,Springboot具体装配代码如下
从代码中我们可以知道,在这个方法中会遍历整个 spring-boot 项目的 classpath 下 ClassLoader 中所有 jar 包下的 spring.factories文件。也就是说我们可以在自己的 jar 中配置 spring.factories 文件,不会影响到其它地方的配置,也不会被别人的配置覆盖。
Spring Factories 在 Spring Boot 中的应用
在 Spring Boot 的很多包中都能够找到 spring.factories 文件,接下来我们以 spring-boot-autoconfigure 包为例进行介绍
结合前面的内容,可以看出 spring.factories 文件可以将 spring-boot 项目包以外的 bean(即在 pom 文件中添加依赖中的 bean)注册到 spring-boot 项目的 spring 容器。
由于@ComponentScan 注解只能扫描 spring-boot 项目包内的 bean 并注册到 spring 容器中,因此需要 @EnableAutoConfiguration 注解来注册项目包外的bean。而 spring.factories 文件,则是用来记录项目包外需要注册的bean类名
以上是关于spring.factories是什么?有什么用处?的主要内容,如果未能解决你的问题,请参考以下文章
spring boot的spring.factories配置的不同类/以及加载时机
如何通过依赖项禁用通过 spring.factories 注册的 spring 工厂并保留此 spring-boot 依赖项?