Spring Boot 回顧(四):深入理解SpringFactoriesLoader

語言: CN / TW / HK

highlight: a11y-dark theme: smartblue


這是我參與8月更文挑戰的第4天,活動詳情檢視:8月更文挑戰

前言

上一篇我們講到Spring Boot自動裝配的原理,而且通過閱讀原始碼,我們看到其實自動裝配的奧義其實就是SpringFactoriesLoader,今天我們再來揭祕下SpringFactoriesLoader的神祕面紗。

介紹

SpringFactoriesLoader工廠的載入機制類似java提供的SPI機制一樣,是Spring提供的一種載入方式。只需要在classpath路徑下新建一個檔案META-INF/spring.factories,並在裡面按照Properties格式填寫好介面和實現類即可通過SpringFactoriesLoader來例項化相應的Bean。其中key可以是介面、註解、或者抽象類的全名。value為相應的實現類,當存在多個實現類時,用“,”進行分割。其實關於spring.factories的實現也是Spring Boot中約定優於配置的體現,可以說,整個Spring Boot是遵循約定優於配置這個理念產生的,因此它才能如此的快而且簡單。

原理

先放一張SpringFactoriesLoader工作的流程圖

mermaid graph LR A[Start] -->B{查詢快取}--> |不存在| 1[讀取指定路徑下的資原始檔]-->C[構建Properties物件] -->D[獲取指定key對應的value值]-->E[逗號分割value值]-->F[儲存結果到快取]-->2[依次例項化物件]-->3[對結果進行排序]-->4[返回結果]-->5[End] B{查詢快取}--> |存在| 2[依次例項化物件] 接下來我們進入重點,開啟SpringFactoriesLoader的原始碼,裡面比較簡單,有2個成員變數和4個方法,其中方法分別是loadFactoriesloadFactoryNamesloadSpringFactoriesinstantiateFactory。我們先看下成員變數 ```java

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static final Map> cache = new ConcurrentReferenceHashMap<>(); 其中第一個是尋找工廠的位置,工廠可以存放在多個jar檔案中,第二個則是自定義用於儲存工廠的快取。然後我們瞭解下其中的方法,首先是`loadFactories`這個方法。java public static List loadFactories(Class factoryType, @Nullable ClassLoader classLoader) { Assert.notNull(factoryType, "'factoryType' must not be null"); // 如果未指定類載入器,則使用預設的 ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } // 獲取指定工廠名稱列表 List factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse); // 如果記錄器Trace跟蹤啟用的話,將工廠名稱列表輸出 if (logger.isTraceEnabled()) { logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames); } // 建立結果集 List result = new ArrayList<>(factoryImplementationNames.size()); for (String factoryImplementationName : factoryImplementationNames) { // 例項化工廠類,並新增到結果集中 result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse)); } // 對結果集列表進行排序 AnnotationAwareOrderComparator.sort(result); return result; } `` 可以看到大致的工作流程如下: 1. 通過classLoader去載入工廠獲取其對應類名稱,如果未指定類載入器,則使用預設的; 2. 通過instantiateFactory方法例項化工廠類,並新增到結果集中; 3. 通過AnnotationAwareOrderComparator#sort方法對工廠進行排序; 接著我們看下loadFactoryNames,這個比較簡單,主要邏輯都在loadSpringFactories,原始碼就不放上來了,其邏輯大致是讀取 classpath上 所有的 jar 包中的所有 META-INF/spring.factories屬 性檔案,找出其中定義的匹配型別 factoryClass 的工廠類,然後並返回這些工廠類的名字列表,注意是包含包名的全限定名。最後是instantiateFactory`方法,它的主要作用就是例項化Bean物件

總結

以上我們通過閱讀了SpringFactoriesLoader的相關程式碼加深了對其理解,總結起來就是SpringFactoriesLoader是用於Spring框架內部的通用工廠載入機制。SpringFactoriesLoader通過loadFactories方法來載入並例項化來自FACTORIES_RESOUCE_LOCATION路徑中的檔案給定的工廠型別,而這些檔案可能包含在類路徑的jar包中。這些檔案通常都命名為spring.factories,並且都是以properties屬性作為格式,檔案中key表示的是介面或者抽象類的全限定名稱,而值是以逗號分隔的實現類的名稱列表。