Spring.factories

目录

1.概述

[2.Spring Boot的扩展机制之Spring Factories](#2.Spring Boot的扩展机制之Spring Factories)

[2.1什么是 SPI机制](#2.1什么是 SPI机制)

[2.2 Spring Boot中的SPI机制](#2.2 Spring Boot中的SPI机制)

[2.3 Spring Factories实现原理是什么](#2.3 Spring Factories实现原理是什么)

[2.4 Spring Factories在Spring Boot中的应用](#2.4 Spring Factories在Spring Boot中的应用)

3.用法及配置Bean

[3.1 ApplicationContextInitializer](#3.1 ApplicationContextInitializer)

[3.2 ApplicationListener](#3.2 ApplicationListener)

[3.3 AutoConfigurationImportListener](#3.3 AutoConfigurationImportListener)

[3.4 AutoConfigurationImportFilter](#3.4 AutoConfigurationImportFilter)

[3.5 EnableAutoConfiguration](#3.5 EnableAutoConfiguration)

[3.6 FailureAnalyzer](#3.6 FailureAnalyzer)

[3.7 TemplateAvailabilityProvider](#3.7 TemplateAvailabilityProvider)

[3.8 EnvironmentPostProcessor?](#3.8 EnvironmentPostProcessor?)


1.概述

在 Spring Boot 项目中,怎样将 pom.xml 文件里面添加的依赖中的 bean 注册到 Spring Boot 项目的 Spring 容器中呢?

你可能会首先想到使用@ComponentScan 注解,遗憾的是 @ComponentScan 注解只能扫描 Spring Boot 项目包内的 bean 并注册到 Spring 容器中,项目依赖包中的 bean 不会被扫描和注册。此时,我们需要使用 @EnableAutoConfiguration 注解来注册项目依赖包中的 bean。而 spring.factories 文件,可用来记录项目包外需要注册的 bean 类名。

使用 spring.factories 文件有什么好处呢?假如我们封装了一个插件,该插件提供给其他开发人员使用。我们可以在spring.factories 文件中指定需要自动注册到 Spring 容器的 bean 和一些配置信息。使用该插件的开发人员只需少许配置,甚至不进行任何配置也能正常使用。

2.Spring Boot的扩展机制之Spring Factories

写在前面:Spring Boot中有一种非常解耦的扩展机制:Spring Factories。这种扩展机制实际上是仿照Java中的SPI扩展机制来实现的。

2.1什么是 SPI机制

SPI的全名为Service Provider Interface.大多数开发人员可能不熟悉,因为这个是针对厂商或者插件的。在java.util.ServiceLoader的文档里有比较详细的介绍。

简单的总结下java SPI机制的思想。我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。

java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。

2.2 Spring Boot中的SPI机制

在Spring中也有一种类似与Java SPI的加载机制。它在META-INF/spring.factories文件中配置接口的实现类名称,然后在程序中读取这些配置文件并实例化。

这种自定义的SPI机制是Spring Boot Starter实现的基础。

2.3 Spring Factories实现原理是什么

spring-core包里定义了SpringFactoriesLoader类,这个类实现了检索META-INF/spring.factories文件,并获取指定接口的配置的功能。在这个类中定义了两个对外的方法:

loadFactories 根据接口类获取其实现类的实例,这个方法返回的是对象列表。

loadFactoryNames 根据接口获取其接口类的名称,这个方法返回的是类名的列表。

上面的两个方法的关键都是从指定的ClassLoader中获取spring.factories文件,并解析得到类名列表,具体代码如下↓

复制代码
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    try {
        Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        List<String> result = new ArrayList<String>();
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
            String factoryClassNames = properties.getProperty(factoryClassName);
            result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
        }
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
                "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

从代码中我们可以知道,在这个方法中会遍历整个ClassLoader中所有jar包下的spring.factories文件。也就是说我们可以在自己的jar中配置spring.factories文件,不会影响到其它地方的配置,也不会被别人的配置覆盖。

spring.factories的是通过Properties解析得到的,所以我们在写文件中的内容都是安装下面这种方式配置的:

com.xxx.interface=com.xxx.classname

如果一个接口希望配置多个实现类,可以使用','进行分割。

2.4 Spring Factories在Spring Boot中的应用

在Spring Boot的很多包中都能够找到spring.factories文件,接下来我们以spring-boot包为例进行介绍

在日常工作中,我们可能需要实现一些SDK或者Spring Boot Starter给被人使用时,

我们就可以使用Factories机制。Factories机制可以让SDK或者Starter的使用只需要很少或者不需要进行配置,只需要在服务中引入我们的jar包即可。

3.用法及配置Bean

spring.factories 文件的用法,以及介绍该文件中可以配置那些 Bean。内容如下:

复制代码
## Initializers
org.springframework.context.ApplicationContextInitializer=

com.huangx.springboot.autoconfig.MyApplicationContextInitializer

复制代码
## Application Listeners
org.springframework.context.ApplicationListener=

com.huangx.springboot.autoconfig.MyApplicationListener

复制代码
## Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=

com.huangx.springboot.autoconfig.MyAutoConfigurationImportListener

复制代码
## Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=

com.huangx.springboot.autoconfig.MyConfigurationCondition

复制代码
## Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=

com.huangx.springboot.autoconfig.MyConfiguration

复制代码
## Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=

com.huangx.springboot.autoconfig.MyFailureAnalyzer

复制代码
## Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=

com.huangx.springboot.autoconfig.MyTemplateAvailabilityProvider

复制代码
#后置环境变量处理器
org.springframework.boot.env.EnvironmentPostProcessor=cn.telecom.starter.cloud.LoadProperties

下面将分别介绍各种配置的具体含义:

3.1 ApplicationContextInitializer

该配置项用来配置实现了ApplicationContextInitializer 接口的类,这些类用来实现上下文初始化。配置如下:

复制代码
org.springframework.context.ApplicationContextInitializer=

com.huangx.springboot.autoconfig.MyApplicationContextInitializer

实例代码:

复制代码
public class MyApplicationContextInitializer implements
        ApplicationContextInitializer<ConfigurableApplicationContext> {
 
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("MyApplicationContextInitializer.initialize() " + applicationContext);
    }
 
}

3.2 ApplicationListener

配置应用程序监听器,该监听器必须实现 ApplicationListener 接口。它可以用来监听ApplicationEvent 事件。配置如下:

复制代码
org.springframework.context.ApplicationListener=

com.huangx.springboot.autoconfig.MyApplicationListener

实例代码:

复制代码
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
 
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("MyApplicationListener.onApplicationEvent() " + event);
        if(event instanceof ApplicationStartedEvent) {
            throw new RuntimeException("我故意抛出的错误,仅仅为了触发自定义 MyFailureAnalyzer");
        }
    }
 
}

3.3 AutoConfigurationImportListener

该配置项用来配置自动配置导入监听器,监听器必须实现AutoConfigurationImportListener 接口。该监听器可以监听 AutoConfigurationImportEvent 事件。配置如下:

复制代码
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=

com.huangx.springboot.autoconfig.MyAutoConfigurationImportListener

实例代码:

复制代码
public class MyAutoConfigurationImportListener implements AutoConfigurationImportListener {
 
    @Override
    public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) {
        System.out.println("MyAutoConfigurationImportListener.onAutoConfigurationImportEvent() " + event);
    }
 
}

3.4 AutoConfigurationImportFilter

配置自动配置导入过滤器,过滤器必须实现AutoConfigurationImportFilter 接口。该过滤器用来过滤那些自动配置类可用,配置信息:

复制代码
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=

com.huangx.springboot.autoconfig.MyConfigurationCondition

实例代码:

复制代码
public class MyConfigurationCondition implements AutoConfigurationImportFilter {
 
    @Override
    public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
        System.out.println("MyConfigurationCondition.match() autoConfigurationClasses=" +
                Arrays.toString(autoConfigurationClasses) +
                ", autoConfigurationMetadata=" + autoConfigurationMetadata);
        return new boolean[0];
    }
 
}

3.5 EnableAutoConfiguration

配置自动配置类。这些配置类需要添加@Configuration 注解,配置如下:

复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=

com.huangx.springboot.autoconfig.MyConfiguration

实例代码:

复制代码
@Configuration
public class MyConfiguration {
 
    public MyConfiguration() {
        System.out.println("MyConfiguration()");
    }
 
}

3.6 FailureAnalyzer

配置自定的错误分析类,该分析器需要实现FailureAnalyzer 接口。配置信息:

复制代码
org.springframework.boot.diagnostics.FailureAnalyzer=

com.huangx.springboot.autoconfig.MyFailureAnalyzer

实例代码:

复制代码
/**
 * 自定义自己的错误分析器 FailureAnalyzer
 * @author Administrator 2021/4/1 13:14
 * @version 1.0
 */
public class MyFailureAnalyzer implements FailureAnalyzer {
 
    @Override
    public FailureAnalysis analyze(Throwable failure) {
        System.out.println("MyFailureAnalyzer.analyze() failure=" + failure);
        return new FailureAnalysis("MyFailureAnalyzer execute", "test spring.factories", failure);
    }
 
}

运行效果如下图:

3.7 TemplateAvailabilityProvider

配置模板的可用性提供者,提供者需要实现TemplateAvailabilityProvider 接口,配置如下:

复制代码
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=

com.huangx.springboot.autoconfig.MyTemplateAvailabilityProvider

实例代码:

复制代码
/**
 * 验证指定的模板是否支持
 * @author Administrator 2021/4/1 13:22
 * @version 1.0
 */
public class MyTemplateAvailabilityProvider implements TemplateAvailabilityProvider {
 
    @Override
    public boolean isTemplateAvailable(String view, Environment environment,
                                       ClassLoader classLoader, ResourceLoader resourceLoader) {
        System.out.println("MyTemplateAvailabilityProvider.isTemplateAvailable() view=" +
                view + ", environment=" + environment + ", classLoader=" + classLoader +
                "resourceLoader=" + resourceLoader);
        return false;
    }
 
}

3.8 EnvironmentPostProcessor

环境后置处理器

复制代码
org.springframework.boot.env.EnvironmentPostProcessor=cn.telecom.starter.cloud.LoadProperties

实例代码:

复制代码
public class LoadProperties implements EnvironmentPostProcessor {

    /**
     * 文件名
     */
    private static final String FILE_NAME = "test-starter-cloud.properties";

    /**
     * 版本锁
     */
    private static int version = 0;


    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        // 通过版本号控制解决启动服务时加载两次的问题
        if (version++ == 0) {
            System.out.println("动态加载自定义配置:" + getClass().getName());
            MutablePropertySources sources = environment.getPropertySources();
            // 加载指定的配置文件(效率较高,但需要指定文件)
            loadPropertySource(sources, FILE_NAME);
        }
    }

    /**
     * 加载配置文件资源
     *
     * @param sources
     * @param file
     */
    private void loadPropertySource(MutablePropertySources sources, String file) {
        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            InputStream is = classLoader.getResourceAsStream(file);

            if (is != null) {
                Properties properties = new Properties();
                properties.load(is);
                PropertiesPropertySource propertySource = new PropertiesPropertySource("dynamic", properties);
                sources.addLast(propertySource);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

文章内容来源:

Spring Boot的扩展机制之Spring Factories_伊成的博客-CSDN博客_spring.factories文件

Spring Boot 你不得不会的 spring.factories 配置 - 人人编程网

相关推荐
Data_agent1 小时前
1688获得1688公司档案信息API,python请求示例
开发语言·数据库·python
-大头.1 小时前
Spring Bean作用域深度解析与实战
java·后端·spring
qq_336313931 小时前
java基础-排序算法
java·开发语言·排序算法
豆沙沙包?1 小时前
2025年--Lc298-1019. 链表中的下一个更大节点(栈)--java版
java·数据结构·链表
fengfuyao9851 小时前
匈牙利算法的MATLAB实现
java·算法·matlab
u***B7921 小时前
Navicat for MySQL下载安装教程
数据库·mysql
毕设源码-钟学长1 小时前
【开题答辩全过程】以 基于springboot农科所农作物信息管理系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
周杰伦fans2 小时前
C# 中 SQL Server 数据库调优指南(小白友好版)
开发语言·数据库·c#
u***32432 小时前
Mysql官网下载Windows、Linux各个版本
linux·数据库·mysql