第六節 自動裝配源碼理解

tips:不同版本代碼實現有差異。

前面兩章了解的流程,就是 SpringBoot 自動轉配的核心。

一、自動裝配

1.1 什么是 SpringBoot 自動裝配?

自動裝配是 Spring 框架用來減少配置的顯式需求而引入的一個特性,該特性通過 @Autowired或者@Resource注解實現依賴對象的自動注入。而 Spring Boot 在此基礎上進一步發展,提出了更高級的自動配置(Auto-configuration)概念。SpringBoot 在啟動時會掃描外部 jar 包中的META-INF/spring.factories文件,將文件中配置的類型信息加載到 Spring 容器,對于外部 jar 來說,只需要按照 SpringBoot 定義的標準,就能將自己的功能裝配到 Spring 容器里面。

自動裝配機制是Spring Boot提供的核心特性之一,它極大地提高了開發效率,同時確保了配置的簡潔性和靈活性,實現“約定大于配置”

1.2 自動裝配的工作原理

自動裝配,抓住三個要素。(在前面章節我們已經分析了其核心流程)

  1. @EnableAutoConfiguration
  2. AutoConfigurationImportSelector
  3. spring.factories

AutoConfigurationImportSelector是自動裝配背后的關鍵角色。實現自ImportSelector接口,它會從 META-INF/spring.factories 文件中加載EnableAutoConfiguration指定的配置類。這是通過SpringFactoriesLoader類來實現的,它為Spring Boot自動裝配提供了加載和解析spring.factories文件的能力。

spring.factories: 位于META-INF目錄下的spring.factories文件包含了自動配置類的全限定名列表。當應用啟動時,這些配置類會被加載并根據條件判斷是否應用到當前的應用上下文中。

@EnableAutoConfiguration 作為自動裝配的關鍵。

二、從注解入手

以 mybatis-starter-apply 為例子開始

gitee地址:uzong-starter-learning: 學習 SpringBoot Starter 的工程案例

在我們啟動類上有這樣的注解 SpringBootApplication,它是開啟配置自動裝配的大門鑰匙。

@SpringBootApplication

@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}
}

分析 @SpringBootApplication 注解, 它是多個注解的集成。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}

  • @Configuration: SpringBootConfiguration 等于 Configuration。屬于派生。
  • @EnableAutoConfiguration: 負責激活 SpringBoot 自動裝配機制
  • @ComponentScan: 路徑掃描,該掃描路徑下的 bean 都會被掃描加載,另外可以排除不需要的類。 即激活 @Conponent 的掃描

@SpringBootApplication 等價于上面三個注解。而實現自動裝配的關鍵注解是 @EnableAutoConfiguration

@EnableAutoConfiguration

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
  • @Import 導入 AutoConfigurationImportSelector 配置類,它會加載各種自動化配置類, 它是實現 Starter 的核心
  • @AutoConfigurationPackag: 由AutoConfigurationPackages.Registrar(一個實現了ImportBeanDefinitionRegistrar的類)處理。默認值是啟動類所在的包路徑,默認指定啟動類路徑下的類加載到 Spring 容器。主要邏輯在 AutoConfigurationPackages#register 方法中。 該方法有兩個參數 registry 和 packageNames。packageNames 的值默認是啟動類包所在的路徑。如下所示:

對應的棧幀

registry 即 DefaultListableBeanFactory

Lite && Full (拓展理解)

@Bean 的聲明方式為“輕量模式 Lite”

@Configuration 下聲明的@Bean為“完全模式Full”,存在 cglib 提升

三、AutoConfigurationImportSelector

關于 AutoConfigurationImportSelector, 它實現了 ImportSelector 接口(3.x 開始)

3.1 ImportSelector

public interface ImportSelector {/*** Select and return the names of which class(es) should be imported based on* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.*/String[] selectImports(AnnotationMetadata importingClassMetadata);}

ImportSelector 用于條件性的導入配置類。是Spring框架處理@Import注解的一部分,提供了一種靈活的方式來根據運行時的條件動態決定 Spring 配置; 尤其是在開發自定義自動配置和框架擴展時。

selectImports方法的返回值是一個包含配置類全路徑名稱(Fully qualified names)的字符串數組

ImportSelector可以與注解一起使用,讓開發者在@Configuration注解的類上通過@Import來導入它。它不直接將配置類加入到Spring上下文中,而是提供一種選擇性地導入配置類的方式。

通過ImportSelector,開發者可以在運行時根據條件(比如classpath中是否存在某個類,或者某個屬性是否被定義等)來決定是否導入某些配置。這提供了極大的靈活性,使得 Spring Boot 的自動配置成為可能。

開發一個可插拔的模塊或框架時,ImportSelector可以用來根據應用的配置或依賴來動態導入配置類。Spring Boot 大量使用了ImportSelector來實現自動配置。通過條件檢查,只有當滿足特定條件時,如果類路徑中有特定的類存在,或某些屬性被定義,相關的配置類才會被導入

3.2 DeferredImportSelector

/*** A variation of {@link ImportSelector} that runs after all {@code @Configuration} beans* have been processed. This type of selector can be particularly useful when the selected* imports are {@code @Conditional}.** <p>Implementations can also extend the {@link org.springframework.core.Ordered}* interface or use the {@link org.springframework.core.annotation.Order} annotation to* indicate a precedence against other {@link DeferredImportSelector}s.** @author Phillip Webb* @since 4.0*/
public interface DeferredImportSelector extends ImportSelector {}

A variation of {@link ImportSelector} that runs after all {@code @Configuration} beans

have been processed

DeferredImportSelectorImportSelector的一個擴展接口,它在常規的ImportSelector執行之后稍晚運行。這允許其他的配置類先被處理,使得DeferredImportSelector可以在做決定時考慮到這些先前的配置。

而 AutoConfigurationImportSelector 就是實現了 DeferredImportSelector 該接口。

ImportSelector是Spring框架中一個強大的特性,它為條件化配置和自動配置提供了強大的支撐,是實現靈活、動態導入Spring配置的關鍵機制。通過ImportSelector和它的擴展,Spring實現了一套強大的配置和自動配置機制,極大地簡化了Spring應用的配置工作。

3.3 AutoConfigurationImportSelector

核心方法 selectImports

自動配置機制能夠有條件地導入合適的配置,這對整合第三方庫、開發自定義 starter 或在運行時動態調整配置都是至關重要的

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {// 檢查自動配置是否啟用,如果沒有就直接返回一個空的導入數組。if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}try {// 加載自動配置元數據,可能包括配置類的屬性、條件以及其他相關信息。AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);// 取得 @EnableAutoConfiguration 注解的屬性,這可能含有排除某些配置類的信息。AnnotationAttributes attributes = getAttributes(annotationMetadata);// 獲取所有候選的自動配置類列表。List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);// 移除配置類列表中的重復元素。configurations = removeDuplicates(configurations);// 基于AutoConfigurationMetadata將配置類列表排序。configurations = sort(configurations, autoConfigurationMetadata);// 獲取所有排除的配置類。Set<String> exclusions = getExclusions(annotationMetadata, attributes);// 校驗排除的類是否真正存在于候選配置中,并拋出異常如果有必要。checkExcludedClasses(configurations, exclusions);// 從候選列表中移除所有排除的類。configurations.removeAll(exclusions);// 過濾掉那些不應被導入的配置類。configurations = filter(configurations, autoConfigurationMetadata);// 觸發一系列自動配置導入事件。fireAutoConfigurationImportEvents(configurations, exclusions);// 將配置類列表轉換為字符串數組返回。return StringUtils.toStringArray(configurations);}......
}

3.3 isEnabled

protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass() == AutoConfigurationImportSelector.class) {return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,true);
}
return true;
}

首先調用 isEnabled 方法去判斷自動化配置到底有沒有開啟,可以通過在 application.properties 中配置 spring.boot.enableautoconfiguration=false 來關閉所有的自動化配置。

3.4 getCandidateConfigurations

獲取 claspath\:META-INF/spring.factories 中所有的自動裝配類。

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());Assert.notEmpty(configurations,"No auto configuration classes found in META-INF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;
}

注意:getSpringFactoriesLoaderFactoryClass(), 返回的是 EnableAutoConfiguration.class。

到這里,我們應該知道 spring.factories 的前綴,為什么要定義成org.springframework.boot.autoconfigure.EnableAutoConfiguration 這種固定key了吧。

3.5 removeDuplicates

除候選自動化配置類中重復的類。借助 LinkedHashSet 去除重復

protected final <T> List<T> removeDuplicates(List<T> list) {return new ArrayList<>(new LinkedHashSet<>(list));
}

3.6 getExclusions

	protected Set<String> getExclusions(AnnotationMetadata metadata,AnnotationAttributes attributes) {Set<String> excluded = new LinkedHashSet<>();excluded.addAll(asList(attributes, "exclude"));excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));excluded.addAll(getExcludeAutoConfigurationsProperty());return excluded;}

getExclusions 獲取到所有被排除的自動化配置類。

  1. 當前注解的 exclude 屬性獲取
  2. 當前注解的 excludeName 屬性獲取
  3. getExcludeAutoConfigurationsProperty() 從配置文件中的 spring.autoconfigure.exclude 屬性獲取

3.7 checkExcludedClasses

校驗不是 not auto-configuration 類則不能使用 exclude 方式做類的排除。

3.8 filter

加載完所有的自動化配置類了,但是,這些配置類是否生效,還需要根據當前項目的依賴等進行加載生效。

比如:MybatisAutoConfiguration 是否能夠加載,需要查看當前的依賴是否存在,比如 SqlSessionFactory.class

for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {invokeAwareMethods(filter);boolean[] match = filter.match(candidates, autoConfigurationMetadata);for (int i = 0; i < match.length; i++) {if (!match[i]) {skip[i] = true;skipped = true;}}
}

處理下面兩個注解

  1. ConditionalOnClass
  2. ConditionalOnMissingClass

3.9 其他方法

fireAutoConfigurationImportEvents:于那些想要在導入配置之前進行特定操作的擴展點。

3.10 小結

到這里,關于 SpringBoot 的Starter 能夠進行擴展,來源于 ImportSelector 實現類。 它是能夠實現擴展的核心。AutoConfigurationImportSelector 是 SpringBoot 中的類, 但是 ImportSelector 是springframework core 中的類。

這些 Configuration 類,將使用 ConfigurationClassParse 進行解析。

四、 ConfigurationClassParse

processDeferredImportSelectors 導入配置類。

private void processDeferredImportSelectors() {for (DeferredImportSelectorHolder deferredImport : deferredImports) {ConfigurationClass configClass = deferredImport.getConfigurationClass();try {String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);}......}
}

五、本章小結

本章能夠實現擴展,是來源于 ImportSelector(DeferredImportSelector) 接口。AutoConfigurationImportSelector(SpringBoot) 整好擴展這個類,使得 Starter 模式能夠非常優雅的進行擴展。

那么下一章,我們將進一步擴展理解 ConfigurationClassParse 這部分源碼。

已同步發布到公眾號:面湯放鹽?第六節 自動裝配源碼理解 (qq.com)

掘金賬號:第六節 自動裝配源碼理解 - 掘金 (juejin.cn)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/13930.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/13930.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/13930.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Redis數據庫知識點

Redis set get del keys redis中有哪些數據類型 string 最大512m key層級 redis的key允許有多個單詞形成層級結構&#xff0c;多個單詞之間用‘:’隔開 set get del keys hash 本身在redis中存儲方式就為key-value, 而hash數據結構中value又是一對key-value hset key …

【easyx】快速入門——彈球小游戲(第一代)

目錄 1.需求 2.運動的小球 3.碰到邊緣反彈 4.圓周撞擊或越過邊界反彈 5.繪制和移動擋板 6.小球碰到擋板反彈 7.游戲失敗時該如何處理 8.隨機初始條件 9.完整代碼 我們這一節將結合動畫和鍵盤交互的知識來做一個小游戲 1.需求 我們先看需求:小球在窗體內運動,撞到除…

從入門到精通:詳解Linux環境基礎開發工具的使用

前言 在這篇文章中&#xff0c;我將深入學習和理解Linux環境基礎開發工具的使用。無論你是初學者還是有一定經驗的開發者&#xff0c;相信這篇文章都會對你有所幫助。我們將詳細講解軟件包管理器、編輯器、編譯器、調試器、自動化構建工具以及版本控制工具的使用。 Linux軟件…

后端數據庫開發JDBC編程Mybatis之用基于XML文件的方式映射SQL語句實操

之前的SQL語句是基于注解 以后開發中一般是一個接口對應一個映射文件 書寫映射文件 基本結構 框架 <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.or…

盡在掌握:Android 13 通知新功能詳解

盡在掌握&#xff1a;Android 13 通知新功能詳解 在移動應用開發中&#xff0c;通知扮演著至關重要的角色&#xff0c;它如同應用程序與用戶之間的橋梁&#xff0c;及時傳遞關鍵信息&#xff0c;提升用戶體驗。Android 13 作為最新的安卓版本&#xff0c;在通知方面帶來了諸多…

Rom應用開發遇到得一些小bug

記錄一些細碎得bug ROM時間類問題 問題描述&#xff1a; 設備拔電重啟&#xff0c;ROM時間為默認時間如1970年1月1日&#xff0c;與某些業務場景互斥 問題原因&#xff1a; 后臺接口校驗https證書校驗失敗&#xff0c;要求是2年內得請求頭校驗了時間戳&#xff0c;時間戳過期…

QLExpress入門及實戰總結

文章目錄 1.背景2.簡介3.QLExpress實戰3.1 基礎例子3.2 低代碼實戰3.2.1 需求描述3.2.1 使用規則引擎3.3.2 運行結果 參考文檔 1.背景 最近研究低代碼實現后端業務邏輯相關功能&#xff0c;使用LiteFlow作為流程編排后端service服務, 但是LiteFlow官方未提供圖形界面編排流程。…

使用RAG和文本轉語音功能,我構建了一個 QA 問答機器人

節前&#xff0c;我們星球組織了一場算法崗技術&面試討論會&#xff0c;邀請了一些互聯網大廠朋友、參加社招和校招面試的同學. 針對算法崗技術趨勢、大模型落地項目經驗分享、新手如何入門算法崗、該如何準備、面試常考點分享等熱門話題進行了深入的討論。 匯總合集&…

代碼隨想錄算法訓練營第36期DAY37

DAY37 先二刷昨天的3道題目&#xff0c;每種方法都寫&#xff1a;是否已完成&#xff1a;是。 報告&#xff1a;134加油站的樸素法沒寫對。原因是&#xff1a;在if中缺少了store>0的判斷&#xff0c;只給出了indexi的判斷。前進法沒寫出來。因為忘記了總油量的判斷。Sum。…

基于springboot圖書個性化推薦系統源碼數據庫

基于springboot圖書個性化推薦系統源碼數據庫 本論文主要論述了如何使用JAVA語言開發一個圖書個性化推薦系統&#xff0c;本系統將嚴格按照軟件開發流程進行各個階段的工作&#xff0c;采用B/S架構&#xff0c;面向對象編程思想進行項目開發。在引言中&#xff0c;作者將論述圖…

K8s 運維架構師實戰課程

阿良課程收益 掌握Kubernetes企業運維管理 掌握部署、運維、存儲、網絡、監控、日志、CICD、服務網格等實戰全面搞定&#xff01; 獨立將公司任何項目容器化遷移到K8s平臺 生產環境真實案例 大廠企業實戰經驗 學習最新版、最佳實踐 K8s 運維架構師實戰【初中級】&#xff1a;ht…

docker 方式gost代理搭建以及代理鏈實施

一、項目地址&#xff1a;https://github.com/ginuerzh/gost 二、實施 環境信息 主機名公網IP地址內網IP地址角色beijing101.200.xxx.xxx192.168.0.160單層代理serverbeijing101.200.xxx.xxx192.168.0.160鏈式代理下游serverhk47.238.xxx.xxx172.31.94.207鏈式代理上游serve…

linux誤刪crontab定時任務后的補救措施(隨手記)

起因 想看一眼定時任務的時候&#xff0c;手誤打成了-r&#xff0c;接著我的定時任務就全沒了…… 補救措施 我們都知道&#xff0c;crontab的幾個關鍵目錄中有一個是/var/log/cron&#xff0c;這個目錄記錄了crontab執行的日志。 如果平時沒有備份crontab的習慣的話&#x…

【MySQL精通之路】InnoDB-內存結構-自適應哈希索引

1.作用 自適應哈希索引使InnoDB能夠在具有適當的工作負載組合和足夠的緩沖池內存的系統上執行更像內存中的數據庫&#xff0c;而不會犧牲事務特性或可靠性。 2.設置 自適應哈希索引由innodb_adaptive_hash_index變量啟用 或在服務器啟動時由--skip-innodb-adaptive-has…

VMware 安裝Windows Server 2008 R2

1.下載鏡像 迅雷&#xff1a;ed2k://|file|cn_windows_server_2008_r2_standard_enterprise_datacenter_and_web_with_sp1_x64_dvd_617598.iso|3368839168|D282F613A80C2F45FF23B79212A3CF67|/ 2.安裝過程 自定義名字&#xff0c;點擊【瀏覽】選擇安裝路徑 點擊【瀏覽】選擇前…

鴻蒙應用開發系列 篇三:ArkTS語言

文章目錄 系列文章概述基本語法基本結構概念釋疑聲明式UI描述高級特性自定義組件頁面和自定義組件生命周期狀態管理渲染控制ArkTS語言基礎類庫系列文章 鴻蒙應用開發系列 篇一:鴻蒙系統概述 鴻蒙應用開發系列 篇二:鴻蒙系統開發工具與環境

(Oracle)SQL優化基礎(三):看懂執行計劃順序

往期內容&#xff1a; &#xff08;Oracle&#xff09;SQL優化基礎&#xff08;一&#xff09;&#xff1a;獲取執行計劃 &#xff08;Oracle&#xff09;SQL優化基礎&#xff08;二&#xff09;&#xff1a;統計信息 獲取到執行計劃后&#xff0c;對于新手朋友來講可能不知道…

Qt筆記:動態處理多個按鈕點擊事件以更新UI

問題描述 在開發Qt應用程序時&#xff0c;經常需要處理多個按鈕的點擊事件&#xff0c;并根據點擊的按鈕來更新用戶界面&#xff08;UI&#xff09;&#xff0c;如下圖。例如&#xff0c;你可能有一個包含多個按鈕的界面&#xff0c;每個按鈕都與一個文本框和一個復選框相關聯…

基于springboot+vue+Mysql的逍遙大藥房管理系統

開發語言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服務器&#xff1a;tomcat7數據庫&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;數據庫工具&#xff1a;Navicat11開發軟件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

Flutter 中的 FormField 小部件:全面指南

Flutter 中的 FormField 小部件&#xff1a;全面指南 在Flutter的世界里&#xff0c;表單是用戶輸入數據的基本方式之一。FormField是一個強大的小部件&#xff0c;它將表單字段的創建、驗證和管理集成到了一個易于使用的抽象中。本文將為您提供一個全面的指南&#xff0c;幫助…