Bootstrap

一篇博客搞懂spring.factories的基本概念和使用

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
;