springboot啟動流程 (3) 自動裝配

来源:https://www.cnblogs.com/xugf/archive/2023/06/21/17495963.html
-Advertisement-
Play Games

在SpringBoot中,EnableAutoConfiguration註解用於開啟自動裝配功能。 本文將詳細分析該註解的工作流程。 # EnableAutoConfiguration註解 啟用SpringBoot自動裝配功能,嘗試猜測和配置可能需要的組件Bean。 自動裝配類通常是根據類路徑和定義 ...


在SpringBoot中,EnableAutoConfiguration註解用於開啟自動裝配功能。

本文將詳細分析該註解的工作流程。

EnableAutoConfiguration註解

啟用SpringBoot自動裝配功能,嘗試猜測和配置可能需要的組件Bean。

自動裝配類通常是根據類路徑和定義的Bean來應用的。例如,如果類路徑上有tomcat-embedded.jar,那麼可能需要一個TomcatServletWebServerFactory(除非已經定義了自己的Servlet WebServerFactory Bean)。

自動裝配試圖儘可能地智能化,並將隨著開發者定義自己的配置而取消自動裝配相衝突的配置。開發者可以使用exclude()排除不想使用的配置,也可以通過spring.autoconfig.exclude屬性排除這些配置。自動裝配總是在用戶定義的Bean註冊之後應用。

用@EnableAutoConfiguration註解標註的類所在包具有特定的意義,通常用作預設掃描的包。通常建議將@EnableAutoConfiguration(如果沒有使用@SpringBootApplication註解)放在根包中,以便可以搜索所有子包和類。

自動裝配類是普通的Spring @Configuration類,使用SpringFactoriesLoader機制定位。通常使用@Conditional方式裝配,最常用的是@ConditionalOnClass和@ConditionalOnMissingBean註解。

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * 當類路徑下沒有指定的類時,可以使用這個屬性指定排除的類
	 */
	String[] excludeName() default {};
}

該註解Import了AutoConfigurationImportSelector類,AutoConfigurationImportSelector類實現了DeferredImportSelector介面。

Import註解和DeferredImportSelector介面在之前的"Spring @Import註解源碼分析"中詳細分析過,此處在介紹它們,只分析AutoConfigurationImportSelector的工作流程。

AutoConfigurationImportSelector類

DeferredImportSelector介面

A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional.

Implementations can also extend the org.springframework.core.Ordered interface or use the org.springframework.core.annotation.Order annotation to indicate a precedence against other DeferredImportSelectors.

Implementations may also provide an import group which can provide additional sorting and filtering logic across different selectors.

AutoConfigurationGroup類

AutoConfigurationImportSelector的getImportGroup方法返回了AutoConfigurationGroup類。

private static class AutoConfigurationGroup implements 
		DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {

	private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();

	private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();

	// ... 略

	@Override
	public void process(
			AnnotationMetadata annotationMetadata,
			DeferredImportSelector deferredImportSelector) {

		// AutoConfigurationEntry類使用List保存Configuration類
		AutoConfigurationEntry autoConfigurationEntry =
            ((AutoConfigurationImportSelector) deferredImportSelector)
				.getAutoConfigurationEntry(annotationMetadata);

		this.autoConfigurationEntries.add(autoConfigurationEntry);
		for (String importClassName : autoConfigurationEntry.getConfigurations()) {
			this.entries.putIfAbsent(importClassName, annotationMetadata);
		}
	}

	@Override
	public Iterable<Entry> selectImports() {
		// 查找排除的配置類
		Set<String> allExclusions = this.autoConfigurationEntries.stream()
				.map(AutoConfigurationEntry::getExclusions)
				.flatMap(Collection::stream)
				.collect(Collectors.toSet());
		// 所有配置類
		Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
				.map(AutoConfigurationEntry::getConfigurations)
				.flatMap(Collection::stream)
				.collect(Collectors.toCollection(LinkedHashSet::new));
		// 將排除的配置類移除掉
		processedConfigurations.removeAll(allExclusions);

		// 排序
		return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
				.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
				.collect(Collectors.toList());
	}

	// ... 略
}

從上面的代碼可以看出,查找自動裝配類的邏輯在getAutoConfigurationEntry方法中。

getAutoConfigurationEntry方法

從META-INF/spring.factories文件解析EnableAutoConfiguration配置。

META-INF/spring.factories文件示例:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
	AnnotationAttributes attributes = getAttributes(annotationMetadata);
	// 查找自動裝配類
	List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
	// 以下幾行為查找排除類、過濾等操作
	configurations = removeDuplicates(configurations);
	Set<String> exclusions = getExclusions(annotationMetadata, attributes);
	checkExcludedClasses(configurations, exclusions);
	configurations.removeAll(exclusions);
	// 這裡的Filter是從META-INF/spring.factories文件解析出來的
	configurations = getConfigurationClassFilter().filter(configurations);
	// 觸發事件
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}

protected List<String> getCandidateConfigurations(
		AnnotationMetadata metadata, AnnotationAttributes attributes) {
	// 從META-INF/spring.factories文件查找EnableAutoConfiguration配置
	List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
						getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
	return configurations;
}

SpringFactoriesLoader類loadFactoryNames方法

Load the fully qualified class names of factory implementations of the given type from "META-INF/spring.factories", using the given class loader.

public static List<String> loadFactoryNames(Class<?> factoryType, ClassLoader classLoader) {
	String factoryTypeName = factoryType.getName();
	return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
	MultiValueMap<String, String> result = cache.get(classLoader);
	if (result != null) {
		return result;
	}

	try {
		// FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
		// 從類路徑下查找META-INF/spring.factories文件
		Enumeration<URL> urls = (classLoader != null ?
				classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
				ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
		result = new LinkedMultiValueMap<>();
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			UrlResource resource = new UrlResource(url);
			// 獲取properties配置
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			for (Map.Entry<?, ?> entry : properties.entrySet()) {
				String factoryTypeName = ((String) entry.getKey()).trim();
				for (String factoryImplementationName :
                     StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
					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);
	}
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 編寫類時,並非總是要從空白開始。如果要編寫的類時另一個現成類的特殊版本,可使用繼承。一個類繼承另一個類時,它將自動獲得另一個類的所有屬性和方法 原有的類稱為父類,而新類被稱為子類。子類繼承了其父類的所有屬性和方法,同時還可以定義自己的屬性和方法。 繼承語法格式: class 子類名(父類名): # ...
  • ## 1、簡介 SpringBoot不僅繼承了Spring框架原有的優秀特性,而且還通過簡化配置來進一步簡化了Spring應用的整個搭建和開發過程。 在Spring-Boot項目開發中,存在著本模塊的代碼需要訪問外面模塊介面,或外部url鏈接的需求, 比如在apaas開發過程中需要封裝介面在介面中調 ...
  • ## 前言 在C語言中,指針函數和函數指針是強大且常用的工具。它們允許我們以更靈活的方式處理函數和數據,進而擴展程式的功能。 本文將介紹指針函數和函數指針的概念,並講解一些常見的應用示例。 ## 一、人物簡介 - 第一位閃亮登場,有請今後會一直教我們C語言的老師 —— 自在。 ![](https:/ ...
  • 基於java的資源博客論壇系統設計與實現,可適用於java個人博客系統,個人資源博客管理系統,java博客系統,java論壇系統,類似於交友微博,新浪微博,發表動態,筆記博客,個人筆記系統。 ...
  • 引言 在日常使用的有些APP中,想什麼微信,百度地圖,可以可以搜尋附近的人,距離自己多遠,以及在地圖上我們可以搜索附近的某個地點,距離自己的位置。針對這種類似的功能,我們可以通過redis就能實現。 redis在3.2版本之後也提供了地理位置的能力,使用redis可以輕鬆實現查找附近的人 一:附近的 ...
  • # 概念 在Java中,`CountDownLatch`是一個線程同步的輔助類,用於等待其他線程完成操作。如果`CountDownLatch`實例被丟失或無法訪問,可能會導致無法正常使用該對象。這可能會導致等待線程永遠處於等待狀態,無法繼續執行。 如果意外丟失了`CountDownLatch`對象, ...
  • 毋庸諱言,密碼是極其偉大的發明,但拜病毒和黑客所賜,一旦密碼泄露,我們就得絞盡腦汁再想另外一個密碼,但記憶力並不是一個靠譜的東西,一旦遺忘密碼,也會造成嚴重的後果,2023年業界巨頭Google已經率先支持了Passkeys登錄方式,只須在設備上利用PIN碼解鎖、指紋或面部辨識等生物識別方式,即可驗 ...
  • `numpy`提供了簡單靈活的介面,用於優化數據數組的計算。 通用計算最大的優勢在於通過向量化操作,將迴圈推送至`numpy`之下的編譯層,從而取得更快的執行效率。 `numpy`的通用計算讓我們計算數組時就像計算單獨一個變數一樣, 不用寫迴圈去遍曆數組中的各個元素。 比如,對於一般的`python ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...