SpringBoot自動裝配原理詳解
首先對于一個SpringBoot工程來說,最明顯的標志的就是
@SpringBootApplication
它標記了這是一個SpringBoot工程,所以今天的 SpringBoot自動裝配原理也就是從它開始說起。
自動裝配流程
首先我們來看下@SpringBootApplication
這個注解的背后又有什么玄機呢,我們按下 ctrl + 鼠標左鍵,輕輕的點一下,此時見證奇跡的時刻..
我們看到如下優(yōu)雅的代碼:
這其中有兩個比較容易引起我們注意的地方,一個是@SpringBootConfiguration
注解,另一個是@EnableAutoConfiguration
注解;之所以說這個兩個注解比較吸引我們的眼球, 不是因為它們長大的好看,而是因為其他的注解太難看了(主要是因為其他的注解我們都是比較熟悉,即使不知道他們是干什么的,可以肯定更自動裝配是沒有關系的)。 然后我們又伸出了邪惡的小手,開啟了熟悉的操作,按下了Ctrt + 鼠標左鍵,瞪著色咪咪的小眼睛,瞳孔放大了百倍等待著奇跡的出現(xiàn)... 擦... 擦...擦...
什么也沒有...
那我要你有何用,這么頂級的世界級的開源項目,怎么會讓一個沒用的家伙存在呢? 于是動用了上億的腦細胞大軍,經(jīng)過復雜的運算,得出了一個不靠譜的結論:它可能使用來標記這是一個SpringBoot工程的配置。因為SpringBootConfiguration
翻譯過來就是SpringBoot的配置,于是心中又是幾萬只羊駝在萬馬奔騰,大漠飛揚。
氣定神閑之后,秉承著·失敗是成功之母"的信念, 熟練的左手行云流水般的按下了 Ctrl + Table 鍵,回到了最初的的地方。眼睛盯著 @EnableAutoConfiguration
,環(huán)顧左右,在地址欄輸入了谷歌翻譯, 結果顯示 自動裝配。我找的就是你,真是眾里尋他千百度,那人卻在燈火闌珊處。 熟練的按下了 Ctrl +左鍵,迫不及待的想要進入; 心里默默背誦起了《桃花源記》的經(jīng)典詩句 ∶
林盡水源,便得一山,山有小口,仿佛若有光。便舍船,從口入。初極狹,才通人。復行數(shù)十步,豁然開朗
此時此刻心情愉悅,有過前面的經(jīng)歷之后,在面對新的世界時候,我們淡定了許多。 此時大腦高速運轉,沒有再糾結,直搗黃龍,進入了 AutoConfigurationImportSelector.class
類,因為谷歌翻譯告訴我們,這個是自動配置導入選擇器。 于是我們發(fā)現(xiàn)了—片新天地
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } // 獲取自動配置的實體 AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } // 具體用來加載自動配置類得方法 protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); // 獲取候選的配置類,即使后宮佳麗三千,也是要篩選的 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 根據(jù)情況,自動配置需要的配置類和不需要的配置了 configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, ); configurations.removeAll(exclusions); configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); // 返回最終需要的配置 return new AutoConfigurationEntry(configurations, exclusions); } }
而這個自動配置的實體 AutoConfigurationEntry
里面有兩個屬性,configurations
和 exclusions
。
protected static class AutoConfigurationEntry { // 用來存儲需要的配置項 private final List<String> configurations; // 用來存儲排除的配置項 private final Set<String> exclusions; private AutoConfigurationEntry() { this.configurations = Collections.emptyList(); this.exclusions = Collections.emptySet(); } }
在后面可以看到 getAutoConfigurationEntry()
方法返回了一個對象 return new AutoConfigurationEntry(configurations, exclusions);
這里也就是把我們需要的配置都拿到了。
那他是怎么拿到的候選的配置類呢? 我們接著看這個獲取候選配置類的方法 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
進到方法后我們看到下面這個方法具體獲取候選配置類的方法內(nèi)容
這里我們跟著斷點去走,首先進入getSpringFactoriesLoaderFactoryClass()
方法
protected Class<?> getSpringFactoriesLoaderFactoryClass() { // 返回的是EnableAutoConfiguration字節(jié)碼對象 return EnableAutoConfiguration.class; }
接著我們在進入getBeanClassLoader()
方法,這里就是一個類加載器
protected ClassLoader getBeanClassLoader() { return this.beanClassLoader; }
最后我們在進入loadFactoryNames()
方法,這個方法就是根據(jù)剛才的字節(jié)碼文件和類加載器來找到候選的配置類。傳遞過來的字節(jié)碼
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } // 獲取的EnableAutoConfiguration.class的權限定名 //org.springframework.boot.autoconfigure.EnableAutoConfiguration String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); }
如下圖:
最后通過loadSpringFactories()
來獲取到所有的配置類
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { // 緩存加載的配置類 Map<String, List<String>> result = cache.get(classLoader); if (result != null) { return result; } result = new HashMap<>(); try { // 去資源目錄下找 Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) .add(factoryImplementationName.trim()); } } } // Replace all lists with unmodifiable lists containing unique elements result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); // 加載完成放到緩存中 cache.put(classLoader, result); } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } // 返回加載到的配置類 return result; }
這里我們要看下怎么從資源目錄下 FACTORIES_RESOURCE_LOCATION 加載的。下面是加載配置文件的路徑:
也就是項目啟動的時候會去加載所有 META-INF 下的所有的 spring.factories 文件,我們搜一下這個這個文件,我搭建的是一個很簡單的 SpringBoot 工程,它會去這幾個 jar 里面找相關的配置類
但是最后自動裝配的類是這個spring-boot-autoconfigure-2.4.3.RELEASE.jar
而根據(jù)EnabLeAutoConfiguration.class
字節(jié)碼加載的配置類就只有這118自動配置類
小結
實際上SpringBoot的自動裝配原理,其實就是在項目啟動的時候去加載META-INF下的 spring.factories 文件,好像也沒有那么高大上。當然在啟動的過程中還會有其他的配置項的加載,這里咱么直說了自動裝配的加載過程。希望對大家可以有所啟發(fā)。
以上就是SpringBoot自動裝配原理詳解的詳細內(nèi)容,更多關于SpringBoot自動裝配原理的資料請關注腳本之家其它相關文章!
相關文章
java中ConcurrentHashMap的讀操作為什么不需要加鎖
ConcurrentHashMap完全允許多個讀操作并發(fā)進行,讀操作并不需要加鎖。所以下面這篇文章主要給大家介紹了關于java中ConcurrentHashMap的讀操作為什么不需要加鎖的相關資料,需要的朋友可以參考下2018-10-10SharedWorkerGlobalScope屬性數(shù)據(jù)共享示例解析
這篇文章主要為大家介紹了SharedWorkerGlobalScope屬性數(shù)據(jù)共享示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12