Spring.factories
1.概述在 Spring Boot 项目中,怎样将 pom.xml 文件里面添加的依赖中的 bean 注册到 Spring Boot 项目的 Spring 容器中呢?你可能会首先想到使用@ComponentScan 注解,遗憾的是 @ComponentScan 注解只能扫描 Spring Boot 项目包内的 bean 并注册到 Spring 容器中,项目依赖包中的 bean 不会被扫描和注册。此
目录
2.Spring Boot的扩展机制之Spring Factories
2.4 Spring Factories在Spring Boot中的应用
3.1 ApplicationContextInitializer
3.3 AutoConfigurationImportListener
3.4 AutoConfigurationImportFilter
3.7 TemplateAvailabilityProvider
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文件
更多推荐
所有评论(0)