Spring中的spring.factories文件用法(Spring如何加載第三方Bean)
Spring的spring.factories文件用法
在springBoot中,它自動(dòng)掃描包的時(shí)候,只會(huì)掃描自己模塊下的類。
問題
如果我們不想被Spring容器管理的Bean的路徑下不再SpringBoot的包掃描路徑下,怎么辦呢?如何加載別的第三方Bean呢?
解決
首先我們創(chuàng)建一個(gè)工程,另外創(chuàng)建一個(gè)與啟動(dòng)類不在一個(gè)級(jí)別的目錄。
第一種方法就是使用在啟動(dòng)類上加上@Import注解。
@Import(value = {Test.class})
第二種方法就是創(chuàng)建spring.factories文件
現(xiàn)在我們將其改造一下,采用spring.factories的方式去加載Test類,在resources目錄下新建一個(gè)META-INF的目錄,然后再新建一個(gè)spring.factories文件,文件內(nèi)容為:
下面第二條就是我們自己的類的路徑。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=/ com.huawei.it.config.Test
然后在springBoot中的啟動(dòng)類中將@Import注釋掉,啟動(dòng)一下,在控制臺(tái)上就會(huì)發(fā)現(xiàn),我們自己的配置類已經(jīng)加載到Spring容器中去了,所以Spring可以加載一個(gè)工程下的任意一下工程類了。
應(yīng)用
下面就是我在Nacos源碼中看到的,可以看到spring.factories文件中內(nèi)容,與我們自己類加載到Spring容器中是一樣的道理。

SpringBoot的擴(kuò)展機(jī)制之Spring Factories
寫在前面:Spring Boot中有一種非常解耦的擴(kuò)展機(jī)制:Spring Factories。這種擴(kuò)展機(jī)制實(shí)際上是仿照J(rèn)ava中的SPI擴(kuò)展機(jī)制來實(shí)現(xiàn)的。
什么是 SPI機(jī)制
SPI的全名為Service Provider Interface.大多數(shù)開發(fā)人員可能不熟悉,因?yàn)檫@個(gè)是針對(duì)廠商或者插件的。在java.util.ServiceLoader的文檔里有比較詳細(xì)的介紹。
簡單的總結(jié)下java SPI機(jī)制的思想。我們系統(tǒng)里抽象的各個(gè)模塊,往往有很多不同的實(shí)現(xiàn)方案,比如日志模塊的方案,xml解析模塊、jdbc模塊的方案等。面向的對(duì)象的設(shè)計(jì)里,我們一般推薦模塊之間基于接口編程,模塊之間不對(duì)實(shí)現(xiàn)類進(jìn)行硬編碼。一旦代碼里涉及具體的實(shí)現(xiàn)類,就違反了可拔插的原則,如果需要替換一種實(shí)現(xiàn),就需要修改代碼。為了實(shí)現(xiàn)在模塊裝配的時(shí)候能不在程序里動(dòng)態(tài)指明,這就需要一種服務(wù)發(fā)現(xiàn)機(jī)制。
java SPI就是提供這樣的一個(gè)機(jī)制:為某個(gè)接口尋找服務(wù)實(shí)現(xiàn)的機(jī)制。有點(diǎn)類似IOC的思想,就是將裝配的控制權(quán)移到程序之外,在模塊化設(shè)計(jì)中這個(gè)機(jī)制尤其重要。
Spring Boot中的SPI機(jī)制
在Spring中也有一種類似與Java SPI的加載機(jī)制。它在META-INF/spring.factories文件中配置接口的實(shí)現(xiàn)類名稱,然后在程序中讀取這些配置文件并實(shí)例化。
這種自定義的SPI機(jī)制是Spring Boot Starter實(shí)現(xiàn)的基礎(chǔ)。

Spring Factories實(shí)現(xiàn)原理是什么
spring-core包里定義了SpringFactoriesLoader類,這個(gè)類實(shí)現(xiàn)了檢索META-INF/spring.factories文件,并獲取指定接口的配置的功能。在這個(gè)類中定義了兩個(gè)對(duì)外的方法:
loadFactories 根據(jù)接口類獲取其實(shí)現(xiàn)類的實(shí)例,這個(gè)方法返回的是對(duì)象列表。
loadFactoryNames 根據(jù)接口獲取其接口類的名稱,這個(gè)方法返回的是類名的列表。
上面的兩個(gè)方法的關(guān)鍵都是從指定的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);
}
}
從代碼中我們可以知道,在這個(gè)方法中會(huì)遍歷整個(gè)ClassLoader中所有jar包下的spring.factories文件。也就是說我們可以在自己的jar中配置spring.factories文件,不會(huì)影響到其它地方的配置,也不會(huì)被別人的配置覆蓋。
spring.factories的是通過Properties解析得到的,所以我們?cè)趯懳募械膬?nèi)容都是安裝下面這種方式配置的:
com.xxx.interface=com.xxx.classname
如果一個(gè)接口希望配置多個(gè)實(shí)現(xiàn)類,可以使用','進(jìn)行分割。
Spring Factories在Spring Boot中的應(yīng)用
在Spring Boot的很多包中都能夠找到spring.factories文件,接下來我們以spring-boot包為例進(jìn)行介紹

在日常工作中,我們可能需要實(shí)現(xiàn)一些SDK或者Spring Boot Starter給被人使用時(shí), 我們就可以使用Factories機(jī)制。Factories機(jī)制可以讓SDK或者Starter的使用只需要很少或者不需要進(jìn)行配置,只需要在服務(wù)中引入我們的jar包即可。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
使用Java將字節(jié)數(shù)組轉(zhuǎn)成16進(jìn)制形式的代碼實(shí)現(xiàn)
在很多場(chǎng)景下,需要進(jìn)行分析字節(jié)數(shù)據(jù),但是我們存起來的字節(jié)數(shù)據(jù)一般都是二進(jìn)制的,這時(shí)候就需要我們將其轉(zhuǎn)成16進(jìn)制的方式方便分析,本文主要介紹如何使用Java將字節(jié)數(shù)組格式化成16進(jìn)制的格式并輸出,需要的朋友可以參考下2024-05-05
SpringMVC Mock測(cè)試實(shí)現(xiàn)原理及實(shí)現(xiàn)過程詳解
這篇文章主要介紹了SpringMVC Mock測(cè)試實(shí)現(xiàn)原理及實(shí)現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
EasyExcel實(shí)現(xiàn)讀寫Excel文件的示例代碼
EasyExcel是阿里巴巴開源的一個(gè)excel處理框架,以使用簡單、節(jié)省內(nèi)存著稱。它可以在盡可能節(jié)約內(nèi)存的情況下支持讀寫百M(fèi)的Excel,所以本文就將利用它實(shí)現(xiàn)讀寫Excel文件,感興趣的可以了解一下2022-08-08
Java實(shí)現(xiàn)多任務(wù)執(zhí)行助手
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)多任務(wù)執(zhí)行助手,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
Java?Optional避免空指針異常的實(shí)現(xiàn)
空指針異常一直是困擾開發(fā)者的常見問題之一,本文主要介紹了Java?Optional避免空指針異常的實(shí)現(xiàn),幫助開發(fā)者編寫更健壯、可讀性更高的代碼,減少因空值處理不當(dāng)而引發(fā)的錯(cuò)誤,感興趣的可以了解一下2025-04-04
Stream distinct根據(jù)list某個(gè)字段去重的解決方案
這篇文章主要介紹了Stream distinct根據(jù)list某個(gè)字段去重,stream的distinct去重方法,是根據(jù) Object.equals,和 Object.hashCode這兩個(gè)方法來判斷是否重復(fù)的,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05

