" />

亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring Bean生命周期之BeanDefinition的合并過(guò)程詳解

 更新時(shí)間:2022年03月04日 15:23:53   作者:碼農(nóng)的進(jìn)階之路  
這篇文章主要為大家詳細(xì)介紹了Spring Bean生命周期之BeanDefinition的合并過(guò)程,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助

寫(xiě)在前面

注:本文章使用的 SpringBoot 版本為 2.2.4.RELEASE,其 Spring 版本為 5.2.3.RELEASE

前言

書(shū)接上文,BeanDefinition注冊(cè)到IoC容器后,緊接著就是要使用Bean了,要使用必須先要獲取Bean,這里我們就以DefaultListableBeanFactory#getBean方法來(lái)引出本次討論的內(nèi)容:BeanDefinition的合并

通過(guò)前面的章節(jié)我們了解到了BeanDefinition,那什么是BeanDefinition的合并呢?為什么要進(jìn)行合并呢? 帶著這個(gè)問(wèn)題,我們到源碼中去找找答案。

為了使源碼邏輯有個(gè)參照,這里先給出一個(gè)案例,在分析源碼時(shí) 將這個(gè)案例也代入進(jìn)去方便我們理解源碼

BeanDefinition的合并源碼分析

實(shí)體類(lèi)

@Data
public class SuperUser implements Serializable {
    private String address;
    public SuperUser(String address) {
        this.address = address;
    }
    public SuperUser() {
    }
}
@Data
@ToString(callSuper = true)
public class User extends SuperUser {
    private String name;
    private Integer age;
    public User() {
    }
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

基于GenericBeanDefinition注冊(cè)有層次的Bean

public class GenericBeanDefinitionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        //父BeanDefinition
        GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();
        rootBeanDefinition.setBeanClass(SuperUser.class);
        //設(shè)置參數(shù)
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.addPropertyValue("address", "地址");
        rootBeanDefinition.setPropertyValues(propertyValues);
        //子BeanDefinition
        GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
        childBeanDefinition.setBeanClass(User.class);
        //設(shè)置構(gòu)造參數(shù)
        ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();
        argumentValues.addIndexedArgumentValue(0, "我就是我");
        argumentValues.addIndexedArgumentValue(1, 18);
        childBeanDefinition.setConstructorArgumentValues(argumentValues);
        childBeanDefinition.setParentName("superUser");
        //類(lèi)型相同時(shí) 以子類(lèi)為主
        childBeanDefinition.setPrimary(true);
        context.registerBeanDefinition("superUser", rootBeanDefinition);
        context.registerBeanDefinition("user", childBeanDefinition);
        context.refresh();
        User user = context.getBean("user", User.class);
        System.out.println(user);
        SuperUser superUser = context.getBean("superUser", SuperUser.class);
        System.out.println(superUser);
        context.close();
    }
}

在分析源碼時(shí)我們要有側(cè)重點(diǎn),這里會(huì)將不太相關(guān)的邏輯一帶而過(guò)。

AbstractBeanFactory#doGetBean

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
		//將name解析為beanName,如果傳入的是alias,根據(jù)aliasMap進(jìn)行轉(zhuǎn)換,我們?cè)谇懊娼榻B過(guò)了
		final String beanName = transformedBeanName(name);
		Object bean;
		// 如果是單例Bean
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			//省略日志輸出
			// 這里的邏輯是根據(jù)beanName判斷是否為FactoryBea,并采用相應(yīng)方式去處理
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
		//如果不是單例對(duì)象
		else {
			// 對(duì)原型對(duì)象進(jìn)行驗(yàn)證,如果當(dāng)前beanName已經(jīng)在創(chuàng)建中了 拋出異常
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}
			// 獲取父BeanFactory,前面我們介紹過(guò)了 BeanFactory允許有層級(jí),可已存在父BeanFactory
			BeanFactory parentBeanFactory = getParentBeanFactory();
			//如果存在父BeanFactory 去父BeanFactory中查找bean
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				//省略去父BeanFactory查找Bean的過(guò)程
			}
			//typeCheckOnly默認(rèn)為false ,這里將beanName放到alreadyCreated集合中 表示該Bean正在創(chuàng)建中
			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}
			try {
			    // 這里來(lái)到了我們要重點(diǎn)關(guān)注的地方了,bd的合并 ??
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);
				//如果存在依賴(lài)Bean,需要進(jìn)行依賴(lài)查找	
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					// 省略dependsOn 依賴(lài)查找代碼
				}
				// 這里的if..else if .. else 是根據(jù)scope取值來(lái)的
				//scope=singleton時(shí)
				if (mbd.isSingleton()) {
					//省略單實(shí)例Bean創(chuàng)建過(guò)程
				}
				//scope=prototype時(shí)
				else if (mbd.isPrototype()) {
					//省略Prototype Bean創(chuàng)建過(guò)程
				}
				//scope=request、application、session時(shí)
				else {
					// 省略其他Scope Bean的創(chuàng)建過(guò)程
		}
		if (requiredType != null && !requiredType.isInstance(bean)) {
			//省略類(lèi)型轉(zhuǎn)換代碼
		}
		// 返回創(chuàng)建的Bean
		return (T) bean;
	}

上面的方法實(shí)現(xiàn)比較長(zhǎng)、比較復(fù)雜,這里只對(duì)重要的地方進(jìn)行些注釋說(shuō)明并將與本次討論無(wú)關(guān)的代碼先行進(jìn)行注釋。

下面就進(jìn)入到BeanDefinition的合并邏輯了

//假設(shè)beanName=user
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
		// 檢查緩存,若存在直接返回
		RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
		if (mbd != null) {
			return mbd;
		}
    //getBeanDefinition(beanName)==>實(shí)際上去DefaultListableBeanFactory.beanDefinitionMap中根據(jù)key查找BeanDefinition,這在注冊(cè)階段已經(jīng)放到beanDefinitionMap了。
		return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
	}
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
			throws BeanDefinitionStoreException {
		return getMergedBeanDefinition(beanName, bd, null);
	}
//根據(jù)上面的舉例可知beanName=user,bd是User類(lèi)的BeanDefinition,containingBd=null
protected RootBeanDefinition getMergedBeanDefinition(
			String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
			throws BeanDefinitionStoreException {
		synchronized (this.mergedBeanDefinitions) {
			RootBeanDefinition mbd = null;
			// 嘗試從緩存中拿
			if (containingBd == null) {
				mbd = this.mergedBeanDefinitions.get(beanName);
			}
			if (mbd == null) {
        //如果當(dāng)前BeanDefinition沒(méi)有指定parentName,說(shuō)明其不存在父BeanDefinition,不需要合并。以RootBeanDefinition形式展現(xiàn)
				if (bd.getParentName() == null) {
					// 如果bd是RootBeanDefinition類(lèi)型,直接類(lèi)型轉(zhuǎn)換
					if (bd instanceof RootBeanDefinition) {
						mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
					}
					else {
            //通過(guò)bd屬性構(gòu)造RootBeanDefinition
						mbd = new RootBeanDefinition(bd);
					}
				}
				else {
					// 走到這里說(shuō)明存在parentName,當(dāng)前bd需要與其父bd合并
					BeanDefinition pbd;
					try {
            //得到父BeanName
						String parentBeanName = transformedBeanName(bd.getParentName());
            //!beanName.equals(parentBeanName) 條件成立 說(shuō)明當(dāng)前beanName屬于子bd
						if (!beanName.equals(parentBeanName)) {
              //遞歸地以父bd名稱(chēng) 查找父BeanDefinition。之所以遞歸地查找,是因?yàn)?可能此時(shí)的parentBeanName還有父,實(shí)體類(lèi)存在多重繼承關(guān)系
							pbd = getMergedBeanDefinition(parentBeanName);
						}
						else {
              //走到這里,說(shuō)明beanName.equals(parentBeanName),很有可能是父bd查找BeanDefinition時(shí)走來(lái)的。
              //獲取父BeanFactory,BeanFactory也是有層次的,有父子關(guān)系的,可參見(jiàn)ConfigurableBeanFactory#setParentBeanFactory
							BeanFactory parent = getParentBeanFactory();
							if (parent instanceof ConfigurableBeanFactory) {
								pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
							}
							else {
								throw new NoSuchBeanDefinitionException(parentBeanName,
										"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
										"': cannot be resolved without an AbstractBeanFactory parent");
							}
						}
					}
					catch (NoSuchBeanDefinitionException ex) {
						throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
								"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
					}
					// pbd是父BeanDefinition,由其構(gòu)造為RootBeanDefinition
					mbd = new RootBeanDefinition(pbd);
          //bd是子BeanDefinition,主要是繼承父類(lèi)的屬性,并覆蓋與父類(lèi)同名的屬性,有興趣的可以看一下overrideFrom方法實(shí)現(xiàn)
					mbd.overrideFrom(bd);
				}
				// 如果父bd未指定scope,則設(shè)置默認(rèn)值
				if (!StringUtils.hasLength(mbd.getScope())) {
					mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
				}
				//由于containingBd=null 這里就不看了
				if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
					mbd.setScope(containingBd.getScope());
				}
				if (containingBd == null && isCacheBeanMetadata()) {
					this.mergedBeanDefinitions.put(beanName, mbd);
				}
			}
			//最終返回根據(jù)當(dāng)前beanName找到的bd
			return mbd;
		}
	}

分析了上面的源碼,我們?cè)囍偨Y(jié)一下:

1、如果不存在parentName,即不需要被合并,直接將bd轉(zhuǎn)為RootBeanDefinition 返回即可

2、如果存在parentName

  • 先根據(jù)parentName 找到父bd,若實(shí)體存在多級(jí)繼承關(guān)系,則需要遞歸地查找。
  • 將父bd轉(zhuǎn)為RootBeanDefinition,并將子bd與父bd進(jìn)行合并
  • 設(shè)置一些其他屬性

總結(jié)

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

相關(guān)文章

  • 快速了解Spring Boot

    快速了解Spring Boot

    這篇文章主要介紹了快速了解Spring Boot,介紹了其環(huán)境準(zhǔn)備,URL中的變量以及模板渲染等內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • Springboot自動(dòng)掃描包路徑來(lái)龍去脈示例詳解

    Springboot自動(dòng)掃描包路徑來(lái)龍去脈示例詳解

    這篇文章主要介紹了Springboot自動(dòng)掃描包路徑來(lái)龍去脈示例詳解,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Java 接口和抽象類(lèi)的區(qū)別詳解

    Java 接口和抽象類(lèi)的區(qū)別詳解

    在面向?qū)ο缶幊讨?,抽象?lèi)和接口是兩個(gè)經(jīng)常被用到的語(yǔ)法概念,是面向?qū)ο笏拇筇匦?,以及很多設(shè)計(jì)模式、設(shè)計(jì)思想、設(shè)計(jì)原則編程實(shí)現(xiàn)的基礎(chǔ)。本文將主要講解二者的區(qū)別
    2021-05-05
  • Java實(shí)現(xiàn)郵件發(fā)送QQ郵箱帶附件

    Java實(shí)現(xiàn)郵件發(fā)送QQ郵箱帶附件

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)郵件發(fā)送QQ郵箱帶附件功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • SpringBoot分頁(yè)查詢(xún)功能的實(shí)現(xiàn)方法

    SpringBoot分頁(yè)查詢(xún)功能的實(shí)現(xiàn)方法

    在實(shí)際的項(xiàng)目開(kāi)發(fā)過(guò)程中,分頁(yè)顯示是很常見(jiàn)的頁(yè)面布局,所以學(xué)習(xí)如何實(shí)現(xiàn)分頁(yè)也是必要的,下面這篇文章主要給大家介紹了關(guān)于SpringBoot分頁(yè)查詢(xún)功能的實(shí)現(xiàn)方法,需要的朋友可以參考下
    2022-06-06
  • java中用float時(shí),數(shù)字后面加f,這樣是為什么你知道嗎

    java中用float時(shí),數(shù)字后面加f,這樣是為什么你知道嗎

    這篇文章主要介紹了java用float時(shí),數(shù)字后面加f,這樣是為什么你知道嗎?具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 二叉樹(shù)基本操作之遞歸和非遞歸遍歷、分支節(jié)點(diǎn)數(shù)詳解

    二叉樹(shù)基本操作之遞歸和非遞歸遍歷、分支節(jié)點(diǎn)數(shù)詳解

    這篇文章主要介紹了二叉樹(shù)基本操作之遞歸和非遞歸遍歷、分支節(jié)點(diǎn)數(shù)詳解,二叉樹(shù)是由n(n>=0)個(gè)結(jié)點(diǎn)的有限集合構(gòu)成,此集合或者為空集,或者由一個(gè)根結(jié)點(diǎn)及兩棵互不相交的左右子樹(shù)組成,并且左右子樹(shù)都是二叉樹(shù),需要的朋友可以參考下
    2023-09-09
  • java迭代器移除元素出現(xiàn)并發(fā)修改異常的原因及解決

    java迭代器移除元素出現(xiàn)并發(fā)修改異常的原因及解決

    這篇文章主要介紹了java迭代器移除元素出現(xiàn)并發(fā)修改異常的原因及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • Java壓縮文件工具類(lèi)ZipUtil使用方法代碼示例

    Java壓縮文件工具類(lèi)ZipUtil使用方法代碼示例

    這篇文章主要介紹了Java壓縮文件工具類(lèi)ZipUtil使用方法代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-11-11
  • 詳解spring 配置的兩種方式:JAVA配置和注解配置

    詳解spring 配置的兩種方式:JAVA配置和注解配置

    這篇文章主要介紹了詳解spring 配置的兩種方式:JAVA配置和注解配置,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06

最新評(píng)論