" />

最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Bean生命周期之Bean的注冊詳解

 更新時間:2022年03月04日 14:50:44   作者:碼農(nóng)的進(jìn)階之路  
這篇文章主要為大家詳細(xì)介紹了Spring Bean生命周期之Bean的注冊,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

前言

上篇文章介紹了Bean元信息的配置與解析過程,限于篇幅Bean注冊過程就沒展開。

這里主要圍繞BeanDefinitionReaderUtils#registerBeanDefinition展開分析下Bean注冊過程

public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {
		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}

上面無論是注冊bd還是建立alias-beanName之間的關(guān)系,均用到了BeanDefinitionRegistry,因此我們就以它為突破口來展開

BeanFactory的繼承體系

在這里插入圖片描述

對圖中常用接口或類進(jìn)行說明:

  • ListableBeanFactory 集合類型BeanFactory 提供一種可以查找所有Bean實例的能力
    • getBeanNamesForType(Class) 根據(jù)類型去查找Bean名稱列表不會強(qiáng)制Bean的初始化,可從源碼中看出來
    • getBeansOfType(Class) 根據(jù)類型去查找Bean實例列表,會強(qiáng)制Bean的初始化,可從源碼中看出來
    • getBeanNamesForAnnotation(Class) 根據(jù)注解類型獲取Bean名稱列表
    • getBeansWithAnnotation(Class) 根據(jù)注解類型獲取Bean實例列表
    • findAnnotationOnBean(String,Class) 根據(jù)指定名稱+標(biāo)注類型獲取Bean實例
  • Hierarchical([?ha???rɑ?k?kl])BeanFactory 層次性BeanFactory,有父子容器的概念,可在ConfigurableListableBeanFactory設(shè)置其父容器
    • getParentBeanFactory() 獲取父容器
    • boolean containsLocalBean(String name) 在當(dāng)前容器中查找是否存在該名稱的Bean實例
  • SingletonBeanRegistry 單實例BeanFactory,與單實例有關(guān)
  • ConfigurableBeanFactory 可配置的BeanFactory,這個一般不用于應(yīng)用程序,是給其他BeanFactory擴(kuò)展用的。的確,定義了很多配置方法
  • ConfigurableListableBeanFactory 可配置的集合類型的BeanFactory
  • AutowireCapableBeanFactory 提供具有自動裝配能力的BeanFactory

透過繼承體系可以看出,BeanDefinitionRegistry的實現(xiàn)類是DefaultListableBeanFactory,該類同時實現(xiàn)了諸多接口,可謂是BeanFactory中集大成者,因此我們到DefaultListableBeanFactory中閱讀下bd注冊及別名注冊的源碼

Bean的注冊

先來分析下DefaultListableBeanFactory的幾個重要的成員屬性

// 這個實質(zhì)上就是IoC容器中Bean的載體,沒錯 它很重要,但它是無序的
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
//它代表了bd名稱的集合,它是有序的 遵循bd注冊的順序
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
// 這是已創(chuàng)建bd名稱的集合,在doGetBean方法根據(jù)beanName創(chuàng)建Bean時,beanName會被加到此集合中
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));

上面兩個屬性都比較重要,兩者結(jié)合使用的話可以實現(xiàn)bd的順序訪問(其實就是遍歷beanDefinitionNames集合時,使用beanDefinitionMap去獲取bd)

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
		//對beanName、bd進(jìn)行非空驗證
		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");
		//如果bd是AbstractBeanDefinition類型,則對bd進(jìn)行驗證(一般情況下 我們場景的bd都是繼承自AbstractBeanDefinition的)
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
			   //bd驗證
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				//省略異常代碼
			}
		}
		//從beanDefinitionMap根據(jù)beanName取bd
		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		//如果beanName名稱的bd已經(jīng)存在
		if (existingDefinition != null) {
			//如果不允許Bean被重新注冊 則拋出異常,這里默認(rèn)值是true
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			//如果已被注冊bd的角色值小于當(dāng)前待注冊bd的角色值 
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
				// 省略日志輸出
			}
			//如果已注冊的同名bd 與本次注冊的bd不相同
			else if (!beanDefinition.equals(existingDefinition)) {
				//省略日志輸出
			}
			else {
				//省略日志輸出
			}
			//將beanName-bd鍵值對放入beanDefinitionMap集合
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			//流程走到這里 說明在beanDefinitionMap中不存在同名bd
			//條件成立 說明alreadyCreated不為空 即有bd已被創(chuàng)建
			if (hasBeanCreationStarted()) {
				// 如果在此之間 有bean正在被創(chuàng)建 則這里進(jìn)行加鎖處理
				synchronized (this.beanDefinitionMap) {
				    //將beanName-bd鍵值對放入beanDefinitionMap集合
					this.beanDefinitionMap.put(beanName, beanDefinition);
					//將beanName添加到beanDefinitionNames集合中
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					//如果beanName是手動注冊的單例Bean名稱,則更新manualSingletonNames
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
						//這里從集合中刪除的原因個人理解:
						//manualSingletonNames記錄的是在registerSingleton時被添加的單實例beanName,而這里注入的不是單實例Bean。因為manualSingletonNames包含了此beanName,因此需要剔除
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				//如果沒有bean在被創(chuàng)建
				//將beanName-bd鍵值對放入beanDefinitionMap集合
				this.beanDefinitionMap.put(beanName, beanDefinition);
				//將beanName添加到集合中
				this.beanDefinitionNames.add(beanName);
				//這里從manualSingletonNames中剔除,個人理解為拖地操作,畢竟若集合中沒有此beanName 也remove不了
				this.manualSingletonNames.remove(beanName);
			}
			//這個集合表示凍結(jié)配置時緩存的beanName集合,暫時未理解透此集合的用途
			this.frozenBeanDefinitionNames = null;
		}
		//如果已存在同名bd或已存在同名的單例對象,則重置所有已被緩存的同名的bd數(shù)據(jù),因此這里bd注冊成功后,肯定后續(xù)還會再生成Bean的
		if (existingDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
	}

其實分析下來發(fā)現(xiàn)Bean注冊的過程還是比較容易理解的,下面試著總結(jié)一下:

  • 若bd未被注冊過,則將bd信息存入BeanDefinitionMap等集合中
  • 若bd已被注冊過,允許覆蓋注冊的情況下,將bd信息存入BeanDefinitionMap等集合中,并清除已被緩存的同名bd信息

下面看一下清除bd信息的代碼邏輯

protected void resetBeanDefinition(String beanName) {
		// 如果此bd屬于被合并的BeanDefinition,則這里將其從MergeBeanDefinition集合中剔除
		clearMergedBeanDefinition(beanName);
		// 如果已存在同名的單例對象 則銷毀,具體細(xì)節(jié)先不展開
		destroySingleton(beanName);
		// 這里for循環(huán)邏輯與MergeBeanDefinition相關(guān),如果存在MergedBeanDefinitionPostProcessor,則重置此bd
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			if (processor instanceof MergedBeanDefinitionPostProcessor) {
				((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName);
			}
		}
		// BeanDefinition運(yùn)行有層級的,如果此bd擁有多個父級bd,那么這里遞歸地重置其父bd
		for (String bdName : this.beanDefinitionNames) {
			if (!beanName.equals(bdName)) {
				BeanDefinition bd = this.beanDefinitionMap.get(bdName);
				if (beanName.equals(bd.getParentName())) {
					resetBeanDefinition(bdName);
				}
			}
		}
	}

alias別名的注冊

看了Bean的注冊,再來看別名的注冊 發(fā)現(xiàn)流程比較清晰,基本上一目了然。

//注意 這里的name 不要具象為beanName,雖然我們是從建立beanName--alias關(guān)系出發(fā)追溯到這里的
public void registerAlias(String name, String alias) {
		//對name、alias進(jìn)行斷言驗證
		Assert.hasText(name, "'name' must not be empty");
		Assert.hasText(alias, "'alias' must not be empty");
		synchronized (this.aliasMap) {
			//如果別名與beanName相同,那別名就沒有必要存在了,因此選擇直接從this.aliasMap中移除此別名
			if (alias.equals(name)) {
				this.aliasMap.remove(alias);
				//省略日志輸出
			}
			else {
			   //從aliasMap中根據(jù)別名獲取name
				String registeredName = this.aliasMap.get(alias);
				if (registeredName != null) {
				    //如果已存在的registeredName與此此要注冊的name一致,那就沒必要注冊了
					if (registeredName.equals(name)) {
						return;
					}
					//流程走到這里,說明同一個別名,對應(yīng)兩個name,如果不允許alias覆蓋 則拋出異常
					if (!allowAliasOverriding()) {
						//省略異常及日志輸出
				}
				//這里對alias進(jìn)行循環(huán)檢查,避免出現(xiàn)A的別名是B,B的別名是A的情況
				checkForAliasCircle(name, alias);
				//將alias--name 放入aliasMap
				this.aliasMap.put(alias, name);
				//省略日志輸出
			}
		}
	}

alias與beanName的映射關(guān)系,為根據(jù)名稱查找Bean又提供了一種思路。就是說除了根據(jù)beanName外,也可以根據(jù)alias去查找Bean。

這部分源碼如下

//name可以是beanName,也可以是alias
public String canonicalName(String name) {
		//局部變量賦值
		String canonicalName = name;
		// Handle aliasing...
		String resolvedName;
		do {
		    //如果從aliasMap中能根據(jù)alias分析出beanName
			resolvedName = this.aliasMap.get(canonicalName);
			if (resolvedName != null) {
				canonicalName = resolvedName;
			}
		}
		while (resolvedName != null);
		// 無論入?yún)ame是beanName還是alias,這里返回的都應(yīng)該是beanName了
		return canonicalName;
	}

總結(jié)

好了,這篇主要分析了BeanDefinition的注冊,順帶著也說了別名的注冊情況。既然BeanDefinition已經(jīng)注冊完成,那緊接著就是BeanDefinition的實例化過程了,這個放到下次分析吧。

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!     

相關(guān)文章

  • SpringCloud邁向云原生的步驟

    SpringCloud邁向云原生的步驟

    這篇文章主要介紹了SpringCloud怎么邁向云原生,通過本文我們來梳理一下Spring?Cloud的前世今生,以及未來云原生發(fā)展的趨勢,可以給這些RPC框架的演進(jìn)帶來一些啟發(fā),感興趣的朋友跟隨小編一起看看吧
    2022-10-10
  • SpringBoot+thymeleaf+Echarts+Mysql 實現(xiàn)數(shù)據(jù)可視化讀取的示例

    SpringBoot+thymeleaf+Echarts+Mysql 實現(xiàn)數(shù)據(jù)可視化讀取的示例

    本文主要介紹了SpringBoot+thymeleaf+Echarts+Mysql 實現(xiàn)數(shù)據(jù)可視化讀取的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • SpringBoot多模塊打包部署Docker的項目實戰(zhàn)

    SpringBoot多模塊打包部署Docker的項目實戰(zhàn)

    本文通過介紹最常見的Maven管理的Spring Boot項目多模塊打包部署Docker來介紹一下項目部署過程中操作流程和幾個需要注意的點(diǎn),具有一定的參加價值,感興趣的可以了解一下
    2023-08-08
  • Java使用JaCoCo進(jìn)行代碼覆蓋率分析的操作指南

    Java使用JaCoCo進(jìn)行代碼覆蓋率分析的操作指南

    JaCoCo是一個開源的 Java 代碼覆蓋率工具,廣泛應(yīng)用于測試過程中,它可以幫助開發(fā)者分析測試代碼的覆蓋情況,在本文中,我們將介紹 JaCoCo 的基本功能、如何集成到 Maven 項目中,以及通過具體案例展示如何生成覆蓋率報告,需要的朋友可以參考下
    2025-02-02
  • Java生產(chǎn)者和消費(fèi)者例子_動力節(jié)點(diǎn)Java學(xué)院整理

    Java生產(chǎn)者和消費(fèi)者例子_動力節(jié)點(diǎn)Java學(xué)院整理

    生產(chǎn)者-消費(fèi)者(producer-consumer)問題,也稱作有界緩沖區(qū)(bounded-buffer)問題,兩個進(jìn)程共享一個公共的固定大小的緩沖區(qū)。下文通過實例給大家介紹java生產(chǎn)者和消費(fèi)者,感興趣的朋友一起學(xué)習(xí)吧
    2017-05-05
  • Java實現(xiàn)在不同線程中運(yùn)行的代碼實例

    Java實現(xiàn)在不同線程中運(yùn)行的代碼實例

    這篇文章主要介紹了Java實現(xiàn)在不同線程中運(yùn)行的代碼,結(jié)合具體實例形式分析了java多線程操作的相關(guān)實現(xiàn)技巧,需要的朋友可以參考下
    2017-04-04
  • spring-session簡介及實現(xiàn)原理源碼分析

    spring-session簡介及實現(xiàn)原理源碼分析

    這篇文章主要介紹了spring-session簡介及實現(xiàn)原理源碼分析,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • 一文帶你了解Java中IO流與Guava的使用

    一文帶你了解Java中IO流與Guava的使用

    Java為我們提供了非常多的操作IO的接口與類,幫助開發(fā)者實現(xiàn)不同源間的數(shù)據(jù)傳輸,比如硬盤文件、網(wǎng)絡(luò)傳輸、應(yīng)用調(diào)用間的數(shù)據(jù)交互與傳遞。今天我們來簡單了解下Java中的流?以及在Guava工具包中,針對IO操作做了什么樣的封裝與設(shè)計
    2022-11-11
  • 一文搞懂Spring中Bean的生命周期

    一文搞懂Spring中Bean的生命周期

    這篇文章主要為大家介紹了Spring中Bean生命周期的使用及控制,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)或工作有一定的參考價值,感興趣的可以學(xué)習(xí)一下
    2022-06-06
  • Java正則表達(dá)式API Matcher類方法

    Java正則表達(dá)式API Matcher類方法

    這篇文章主要介紹了Java正則表達(dá)式API Matcher類方法,對Matcher類的一些有用方法進(jìn)行功能對它們進(jìn)行分組展開介紹,需要的朋友可以參考一下
    2022-06-06

最新評論

南华县| 浦江县| 尼玛县| 太谷县| 宝山区| 涡阳县| 呼伦贝尔市| 霍林郭勒市| 乌海市| 浮山县| 涞水县| 德化县| 泸西县| 西乌珠穆沁旗| 剑阁县| 东乡县| 临漳县| 镇赉县| 冷水江市| 广平县| 崇文区| 泽州县| 科技| 壶关县| 武定县| 简阳市| 北海市| 昌邑市| 苏尼特右旗| 高台县| 资源县| 尉氏县| 汾西县| 喜德县| 三河市| 稻城县| 玛多县| 衡山县| 刚察县| 常熟市| 那坡县|