SpringFactoriesLoader類(lèi)作用詳解
SpringFactoriesLoader類(lèi)

介紹
SpringFactoriesLoader類(lèi)的主要作用是通過(guò)類(lèi)路徑下的META-INF/spring.factories文件獲取工廠類(lèi)接口的實(shí)現(xiàn)類(lèi),初始化并保存在緩存中,以供Springboot啟動(dòng)過(guò)程中各個(gè)階段的調(diào)用。Spring的自動(dòng)化配置功能,也與此息息相關(guān)。
SpringFactoriesLoader 工廠加載機(jī)制是 Spring 內(nèi)部提供的一個(gè)約定俗成的加載方式,只需要在模塊的 META-INF/spring.factories 文件中,以 Properties 類(lèi)型(即 key-value 形式)配置,就可以將相應(yīng)的實(shí)現(xiàn)類(lèi)注入 Spirng 容器中。
Properties類(lèi)型格式:
key:value
- key:是全限定名(抽象類(lèi)|接口)
- value:是實(shí)現(xiàn)類(lèi),多個(gè)實(shí)現(xiàn)類(lèi)通過(guò)逗號(hào)進(jìn)行分割
spring boot類(lèi)路徑下: META-INFO/spring.factories

方法
| 返回值 | 方法 | 描述 |
| <T>List<T> | loadFactories(Class<T> factoryType,@Nullable ClassLoader classLoader) | 靜態(tài)方法 根據(jù)接口獲取其實(shí)現(xiàn)類(lèi)的實(shí)例 該方法返回的是實(shí)現(xiàn)類(lèi)對(duì)象列表 |
| List<String> | loadFactoryNames(Class<?>) factoryType,@Nullable ClassLoader classLoader) | 公共靜態(tài)方法 根據(jù)接口獲取其實(shí)現(xiàn)類(lèi)的名稱(chēng) 該方法返回的是實(shí)現(xiàn)類(lèi)的類(lèi)名的列表 |
public final class SpringFactoriesLoader {
//文件位置,可以存在多個(gè)JAR文件中
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
//用來(lái)緩存MultiValueMap對(duì)象
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
private SpringFactoriesLoader() {
}
/**
* 根據(jù)給定的類(lèi)型加載并實(shí)例化工廠的實(shí)現(xiàn)類(lèi)
*/
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryType, "'factoryType' must not be null");
//獲取類(lèi)加載器
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
//加載類(lèi)的全限定名
List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
}
//創(chuàng)建一個(gè)存放對(duì)象的List
List<T> result = new ArrayList<>(factoryImplementationNames.size());
for (String factoryImplementationName : factoryImplementationNames) {
//實(shí)例化Bean,并將Bean放入到List集合中
result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
}
//對(duì)List中的Bean進(jìn)行排序
AnnotationAwareOrderComparator.sort(result);
return result;
}
/**
* 根據(jù)給定的類(lèi)型加載類(lèi)路徑的全限定名
*/
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
//獲取名稱(chēng)
String factoryTypeName = factoryType.getName();
//加載并獲取所有META-INF/spring.factories中的value
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//根據(jù)類(lèi)加載器從緩存中獲取,如果緩存中存在,就直接返回,如果不存在就去加載
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
//獲取所有JAR及classpath路徑下的META-INF/spring.factories的路徑
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
//遍歷所有的META-INF/spring.factories的路徑
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
//將META-INF/spring.factories中的key value加載為Prpperties對(duì)象
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
//key名稱(chēng)
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
//以factoryTypeName為key,value為值放入map集合中
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
//放入到緩存中
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
//實(shí)例化Bean對(duì)象
@SuppressWarnings("unchecked")
private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
try {
Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
throw new IllegalArgumentException(
"Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
}
return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
ex);
}
}
}測(cè)試
resources下新建META-INF/spring.factories

com.moming.service.IStudentService=\
com.moming.service.impl.StudentServiceImpl1,\
com.moming.service.impl.StudentServiceImpl2
創(chuàng)建業(yè)務(wù)層接口及實(shí)現(xiàn)類(lèi)
service/IStudentService
service/impl/StudentServiceImpl1
service/impl/StudentServiceImpl2

測(cè)試

@SpringBootApplication
public class App{
public static void main(String[] args) {
//SpringApplication.run(App.class, args);
List<String> names = SpringFactoriesLoader.loadFactoryNames(IStudentService.class, ClassUtils.getDefaultClassLoader());
for (String name : names) {
System.out.println(name);
}
System.out.println("===================================");
List<IStudentService> iStudentServices = SpringFactoriesLoader.loadFactories(IStudentService.class, ClassUtils.getDefaultClassLoader());
for (IStudentService iStudentService : iStudentServices) {
System.out.println(iStudentService);
System.out.println(iStudentService.game());
}
}
}通過(guò)以上可以證明,SpringFactoriesLoader會(huì)尋找jar包中配置META-INF下的spring.factories配置文件相應(yīng)Key的value,并根據(jù)需要實(shí)例化
到此這篇關(guān)于SpringFactoriesLoader類(lèi)作用詳解的文章就介紹到這了,更多相關(guān)SpringFactoriesLoader內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java開(kāi)發(fā)如何把數(shù)據(jù)庫(kù)里的未付款訂單改成已付款
這篇文章主要介紹了Java開(kāi)發(fā)如何把數(shù)據(jù)庫(kù)里的未付款訂單改成已付款,先介紹MD5算法,簡(jiǎn)單的來(lái)說(shuō),MD5能把任意大小、長(zhǎng)度的數(shù)據(jù)轉(zhuǎn)換成固定長(zhǎng)度的一串字符,實(shí)現(xiàn)思路非常簡(jiǎn)單需要的朋友可以參考下2022-11-11
Java集合之Map接口與實(shí)現(xiàn)類(lèi)詳解
這篇文章主要為大家詳細(xì)介紹了Java集合中的Map接口與實(shí)現(xiàn)類(lèi),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定的幫助,感興趣的可以了解一下2022-12-12
Java實(shí)現(xiàn)MD5加密及解密的代碼實(shí)例分享
如果對(duì)安全性的需求不是太高,MD5仍是使用非常方便和普及的加密方式,比如Java中自帶的MessageDigest類(lèi)就提供了支持,這里就為大家?guī)?lái)Java實(shí)現(xiàn)MD5加密及解密的代碼實(shí)例分享:2016-06-06
使用Idea或Datagrip導(dǎo)入excel數(shù)據(jù)的方法
這篇文章主要介紹了使用Idea或Datagrip導(dǎo)入excel數(shù)據(jù)的方法,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11
SpringBoot實(shí)現(xiàn)圖片識(shí)別文字的四種方式小結(jié)
本文主要介紹了SpringBoot實(shí)現(xiàn)圖片識(shí)別文字的四種方式,包括Tess4J,百度智能云,阿里云,騰訊云這四種,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02
java文件下載設(shè)置中文名稱(chēng)的實(shí)例(response.addHeader)
下面小編就為大家分享一篇java文件下載設(shè)置中文名稱(chēng)的實(shí)例(response.addHeader),具有很好的參考價(jià)值,希望對(duì)大家有所幫助2017-12-12
java實(shí)現(xiàn)事件委托模式的實(shí)例詳解
這篇文章主要介紹了java實(shí)現(xiàn)事件委托模式的實(shí)例詳解的相關(guān)資料,這里提供實(shí)例來(lái)說(shuō)明如何實(shí)現(xiàn)改功能,希望能幫助到大家理解這樣的模式,需要的朋友可以參考下2017-08-08
Maven熱部署devtools的實(shí)現(xiàn)示例
本文主要介紹了Maven熱部署devtools的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
如何通過(guò)Java監(jiān)聽(tīng)MySQL數(shù)據(jù)的變化
對(duì)于二次開(kāi)發(fā)來(lái)說(shuō),很大一部分就找找文件和找數(shù)據(jù)庫(kù)的變化情況,下面這篇文章主要給大家介紹了關(guān)于如何通過(guò)Java監(jiān)聽(tīng)MySQL數(shù)據(jù)的變化的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03
在Spring中基于Java類(lèi)進(jìn)行配置的完整步驟
基于Java配置選項(xiàng),可以編寫(xiě)大多數(shù)的Spring不用配置XML,下面這篇文章主要給大家介紹了關(guān)于在Spring中基于Java類(lèi)進(jìn)行配置的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05

