Java中的Spring.factories文件詳解
1.概述
在 Spring Boot 項目中,怎樣將 pom.xml 文件里面添加的依賴中的 bean 注冊到 Spring Boot 項目的 Spring 容器中呢?
你可能會首先想到使用@ComponentScan 注解,遺憾的是 @ComponentScan 注解只能掃描 Spring Boot 項目包內(nèi)的 bean 并注冊到 Spring 容器中,項目依賴包中的 bean 不會被掃描和注冊。此時,我們需要使用 @EnableAutoConfiguration 注解來注冊項目依賴包中的 bean。而 spring.factories 文件,可用來記錄項目包外需要注冊的 bean 類名。
使用 spring.factories 文件有什么好處呢?假如我們封裝了一個插件,該插件提供給其他開發(fā)人員使用。我們可以在spring.factories 文件中指定需要自動注冊到 Spring 容器的 bean 和一些配置信息。使用該插件的開發(fā)人員只需少許配置,甚至不進行任何配置也能正常使用。
2.Spring Boot的擴展機制之Spring Factories
寫在前面:Spring Boot中有一種非常解耦的擴展機制:Spring Factories。這種擴展機制實際上是仿照Java中的SPI擴展機制來實現(xiàn)的。
2.1什么是 SPI機制
SPI的全名為Service Provider Interface.大多數(shù)開發(fā)人員可能不熟悉,因為這個是針對廠商或者插件的。在java.util.ServiceLoader的文檔里有比較詳細的介紹。
簡單的總結(jié)下java SPI機制的思想。我們系統(tǒng)里抽象的各個模塊,往往有很多不同的實現(xiàn)方案,比如日志模塊的方案,xml解析模塊、jdbc模塊的方案等。面向的對象的設計里,我們一般推薦模塊之間基于接口編程,模塊之間不對實現(xiàn)類進行硬編碼。一旦代碼里涉及具體的實現(xiàn)類,就違反了可拔插的原則,如果需要替換一種實現(xiàn),就需要修改代碼。為了實現(xiàn)在模塊裝配的時候能不在程序里動態(tài)指明,這就需要一種服務發(fā)現(xiàn)機制。
java SPI就是提供這樣的一個機制:為某個接口尋找服務實現(xiàn)的機制。有點類似IOC的思想,就是將裝配的控制權(quán)移到程序之外,在模塊化設計中這個機制尤其重要。
2.2 Spring Boot中的SPI機制
在Spring中也有一種類似與Java SPI的加載機制。它在META-INF/spring.factories文件中配置接口的實現(xiàn)類名稱,然后在程序中讀取這些配置文件并實例化。
這種自定義的SPI機制是Spring Boot Starter實現(xiàn)的基礎。

2.3 Spring Factories實現(xiàn)原理是什么
spring-core包里定義了SpringFactoriesLoader類,這個類實現(xiàn)了檢索META-INF/spring.factories文件,并獲取指定接口的配置的功能。在這個類中定義了兩個對外的方法:
loadFactories 根據(jù)接口類獲取其實現(xiàn)類的實例,這個方法返回的是對象列表。
loadFactoryNames 根據(jù)接口獲取其接口類的名稱,這個方法返回的是類名的列表。
上面的兩個方法的關鍵都是從指定的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解析得到的,所以我們在寫文件中的內(nèi)容都是安裝下面這種方式配置的:
com.xxx.interface=com.xxx.classname
如果一個接口希望配置多個實現(xiàn)類,可以使用’,’進行分割。
2.4 Spring Factories在Spring Boot中的應用
在Spring Boot的很多包中都能夠找到spring.factories文件,接下來我們以spring-boot包為例進行介紹

在日常工作中,我們可能需要實現(xiàn)一些SDK或者Spring Boot Starter給被人使用時,
我們就可以使用Factories機制。Factories機制可以讓SDK或者Starter的使用只需要很少或者不需要進行配置,只需要在服務中引入我們的jar包即可。
3.用法及配置Bean
spring.factories 文件的用法,以及介紹該文件中可以配置那些 Bean。內(nèi)容如下:
## 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
#后置環(huán)境變量處理器 org.springframework.boot.env.EnvironmentPostProcessor=cn.telecom.starter.cloud.LoadProperties
下面將分別介紹各種配置的具體含義:
3.1 ApplicationContextInitializer
該配置項用來配置實現(xiàn)了ApplicationContextInitializer 接口的類,這些類用來實現(xiàn)上下文初始化。配置如下:
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
配置應用程序監(jiān)聽器,該監(jiān)聽器必須實現(xiàn) ApplicationListener 接口。它可以用來監(jiān)聽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("我故意拋出的錯誤,僅僅為了觸發(fā)自定義 MyFailureAnalyzer");
}
}
}3.3 AutoConfigurationImportListener
該配置項用來配置自動配置導入監(jiān)聽器,監(jiān)聽器必須實現(xiàn)AutoConfigurationImportListener 接口。該監(jiān)聽器可以監(jiān)聽 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
配置自動配置導入過濾器,過濾器必須實現(xiàn)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
配置自定的錯誤分析類,該分析器需要實現(xiàn)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
配置模板的可用性提供者,提供者需要實現(xiàn)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
環(huán)境后置處理器
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("動態(tài)加載自定義配置:" + 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();
}
}
}文章內(nèi)容來源:
Spring Boot的擴展機制之Spring Factories_伊成的博客-CSDN博客_spring.factories文件
Spring Boot 你不得不會的 spring.factories 配置 - 人人編程網(wǎng)
到此這篇關于Java中的Spring.factories文件詳解的文章就介紹到這了,更多相關Spring.factories內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringCloud Gateway中斷言路由和過濾器的使用詳解
這篇文章主要介紹了SpringCloud Gateway中斷言路由和過濾器的使用,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-04-04
java實現(xiàn)163郵箱發(fā)送郵件到qq郵箱成功案例
這篇文章主要為大家分享了java實現(xiàn)163郵箱發(fā)送郵件到qq郵箱成功案例,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-05-05
MAC 系統(tǒng)如何使用 Sublime Text 2 直接編譯運行 java 代碼
這篇文章主要介紹了MAC 系統(tǒng)如何使用 Sublime Text 2 直接編譯運行 java 代碼,需要的朋友可以參考下2014-10-10
詳細理解JAVA面向?qū)ο蟮姆庋b,繼承,多態(tài),抽象
這篇文章主要介紹了Java基礎之面向?qū)ο髾C制(多態(tài)、繼承)底層實現(xiàn),文中有非常詳細的代碼示例,對正在學習java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-07-07
如何使用JCTools實現(xiàn)Java并發(fā)程序
這篇文章主要介紹了如何使用JCTools實現(xiàn)Java并發(fā)程序,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下2021-03-03
SpringBoot如何獲取application.properties中自定義的值
這篇文章主要介紹了SpringBoot獲取application.properties中的自定義的值,目錄結(jié)構(gòu)文件代碼給大家列舉的非常詳細,需要的朋友可以參考下2021-09-09

