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.factories机制

spring.factories配置文件的工厂模式

spring boot的spring.factories配置的不同类/以及加载时机

Spring的Factories机制介绍

如何通过依赖项禁用通过 spring.factories 注册的 spring 工厂并保留此 spring-boot 依赖项?

自定义spring boot starter三部曲之三:源码分析spring.factories加载过程