1. 概念
spring.factories 是 Spring 框架中的一个重要配置文件,用于定义和加载 Spring Boot 自动配置类、监听器、环境后处理器等。它通常位于 META-INF 目录下
1.1 springboot 自动配置原理
在应用程序的入口设置了 @SpringBootApplication标签,默认情况下他会扫描所有次级目录。
如果增加了 scanBasePackages属性,就会扫描所有被指定的路径及其次级目录,会扫描 @Component这个注解
所有被扫描到的 @Component,都会成为一个默认的singleton(单例,即一个容器里只有一个对象实体)加入到容器中
。
如下:
- 应用程序的入口设置了 @SpringBootApplication标签,默认情况下他会扫描所有次级目录
- scanBasePackages属性,就会扫描所有被指定的路径及其次级目录:com.xx.scan及其下面目录中的@Component这个注解
@SpringBootApplication( scanBasePackages ={ "com.xx.scan"} ) public class MyAppApplication { public static void main(String[] args) { SpringApplication.run(MyAppApplication.class, args); } }
实现配置:
- @Configuration,这个标签继承了 @Component标签
- 所以,@Configuration会被 @SpringBootApplication扫描到,进而把它和它下面的 @Bean加入容器
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
1.2 spring.factories 作用
在springboot运行时,用于在maven中引用的其他外部包加入容器的过程
spring.factories 文件可以将 spring-boot 项目包以外的 bean(即在 pom 文件中添加依赖中的 bean)注册到 spring-boot 项目的 spring 容器。
ComponentScan 注解只能扫描 spring-boot 项目包内的 bean 并注册到 spring 容器中
,因此需要 @EnableAutoConfiguration 注解来注册项目包外的bean
。
而 spring.factories 文件,则是用来记录项目包外需要注册的bean类名
1.2.1 举例
在springboot运行时,SpringFactoriesLoader 类会去寻找
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
在这里以mybatis-plus为例,去maven的依赖里看它的自动配置类
MybatisPlusAutoConfiguration
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({
SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({
MybatisPlusProperties.class})
@AutoConfigureAfter({
DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean {
}
有上文提到的 @Configuration,还有从application.yml载入自动配置的 @EnableConfigurationProperties({MybatisPlusProperties.class})
springboot只要能扫描到MybatisPlusAutoConfiguration类的 @Configuration注解,其中的所有配置就能自动加入到容器中,这一过程由上面提到的SpringFactoriesLoader 起作用,它会去寻找 “META-INF/spring.factories” 文件,我们可以在 mybatis-plus的依赖中找到它:
配置如下:
#Auto Configure
org.springframework.boot.env.EnvironmentPostProcessor=\
com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.baomidou.mybatisplus.autoconfigure.IdentifierGeneratorAutoConfiguration,\
com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\
com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration
1.3 spring.factories 的妙用
@ComponentScan 注解的作用是扫描 @SpringBootApplication 所在的 Application 类所在的包(basepackage)下所有的 @component 注解(或拓展了 @component 的注解)标记的 bean,并注册到 spring 容器中。
在 Spring Boot 项目中,如果你想要被 Spring 容器管理的 bean 不在 Spring Boot 包扫描路径下,解决方式有两种
- 在 Spring Boot 主类上使用 @Import 注解
- 使用 spring.factories 文件
1.4 SPI机制
Spring Boot 中有一种非常解耦的扩展机制:Spring Factories。
这种扩展机制实际上是仿照Java中的SPI扩展机制来实现的,
- SPI 的全名为 Service Provider Interface
- java SPI 就是提供这样的一个机制:
为某个接口寻找服务实现的机制
在 Spring 中也有一种类似与 Java SPI 的加载机制。
- 在 resources/META-INF/spring.factories 文件中配置接口的实现类名称,然后在程序中读取这些配置文件并实例化。
- 这种自定义的SPI机制是 Spring Boot Starter 实现的基础。
1.5 实现原理
spring-core 包里定义了 SpringFactoriesLoader 类,这个类实现了检索 META-INF/spring.factories 文件,并获取指定接口的配置的功能
。在这个类中定义了两个对外的方法:
- loadFactories 根据接口类获取其实现类的实例,这个方法
返回的是对象列表
。 - loadFactoryNames 根据接口获取其接口类的名称,这个方法
返回的是类名的列表
。
具体实现代码如下:
- 在这个方法中会遍历整个 spring-boot 项目的 classpath 下 ClassLoader 中所有 jar 包下的 spring.factories文件
- 以在自己的 jar 中配置 spring.factories 文件,不会影响到其它地方的配置,也不会被别人的配置覆盖
package org.springframework.core.io.support;
public final class SpringFactoriesLoader {
// 定义了 Spring Factories 文件的位置
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// 日志记录器
private static final Log logger = LogFactory.getLog