溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Spring GetBean的使用流程

發(fā)布時(shí)間:2021-06-26 14:22:07 來(lái)源:億速云 閱讀:140 作者:chen 欄目:大數(shù)據(jù)

本篇內(nèi)容介紹了“Spring GetBean的使用流程”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

一、getBean

?在這之前,先介紹BeanFactory的層次結(jié)構(gòu),如下:

Spring GetBean的使用流程

涉及到的接口和實(shí)現(xiàn)類為:

?AliasRegistry:別名管理接口,定義了別名管理的功能

?SimpleAliasRegistry:AliasRegistry的默認(rèn)實(shí)現(xiàn),內(nèi)部用一個(gè)

?ConcurrentHashMap:管理別名

?SingletonBeanRegistry:?jiǎn)卫龑?shí)例管理接口,定義了單例管理的功能

?DefaultSingletonBeanRegistry:?jiǎn)卫芾韺?shí)現(xiàn)類,內(nèi)部用Map維護(hù)著被實(shí)例化后的所有單例、單例工廠等相關(guān)信息。Map的鍵為bean的唯一標(biāo)識(shí),Spring內(nèi)部成為raw name,一般等同于Bean定義中的id或者name或者別名等,具體規(guī)則可以從上節(jié)BeanDefinition的加載查看,值為相應(yīng)的對(duì)象實(shí)例。這邊需要指出的一點(diǎn)是,對(duì)于bean定義中具有別名意義的字段,如一定情況下的name以及alias字段,只存在于SimpleAliasRegistry維護(hù)的內(nèi)部Map中,通過(guò)遞歸查詢的方式可以從一個(gè)給定的別名查找到指定的id。

?如下,DefaultSingletonBeanRegistry維護(hù)的Map中存在key為testBean,value為TestBean的對(duì)象,SimpleAliasRegistry維護(hù)的Map中存在Key為testBeanAlias1,value為testBean的記錄。當(dāng)通過(guò)testBeanAlias1查找bean時(shí),會(huì)先通過(guò)AliasRegistry查找到testBean,再?gòu)耐ㄟ^(guò)BeanRegistry查找到對(duì)應(yīng)的Bean實(shí)例。

Spring GetBean的使用流程

?FactoryBeanRegistrySupport:增加緩存FactoryBean實(shí)例功能,DefaultSingleBeanRegistry在生成單例后便不再持有對(duì)應(yīng)的FactoryBean

?BeanFactory:定義了Bean容器的基本查詢接口,同時(shí)設(shè)定了以&前綴來(lái)區(qū)別工廠Bean,即如果beanName前面有&則返回對(duì)應(yīng)Bean的工廠Bean對(duì)象而不是該Bean對(duì)象。

?HierarchicalBeanFactory:在BeanFactory接口上增加了父子層級(jí)關(guān)系,以實(shí)現(xiàn)雙親委托。

?ConfigurableBeanFactory:按照規(guī)矩,增加了修改功能的接口,同時(shí)增加了Scope特性,默認(rèn)分為single單例和prototype多例。

?AbstractBeanFactory:BeanFacoty的基本實(shí)現(xiàn)。

?AbstractBeanFactory的getBean方法內(nèi)部調(diào)用了doGetBean,該方法提供了根據(jù)beanName獲取實(shí)例的具體實(shí)現(xiàn),代碼如下(刪除了相關(guān)的注釋和空格):

protected <T> T doGetBean(
			final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {

		/*(1)*/
		final String beanName = transformedBeanName(name);
		Object bean;
		/*(2)*/
		Object sharedInstance = getSingleton(beanName);
		/*(3)*/
		if (sharedInstance != null && args == null) {
			if (logger.isDebugEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
		/*(4)*/
		else {
			/*(5)*/
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}
			/*(6)*/
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				String nameToLookup = originalBeanName(name);
				if (args != null) {
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else {
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
			}
			/*(7)*/
			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}
			try {
				/*(8)*/
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);
				/*(9)*/
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						registerDependentBean(dep, beanName);
						try {
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}
				/*(10)*/
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
						@Override
						public Object getObject() throws BeansException {
							try {
								return createBean(beanName, mbd, args);
							}
							catch (BeansException ex) {
								destroySingleton(beanName);
								throw ex;
							}
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
				/*(11)*/
				else if (mbd.isPrototype()) {
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}
				/*(12)*/
				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
							@Override
							public Object getObject() throws BeansException {
								beforePrototypeCreation(beanName);
								try {
									return createBean(beanName, mbd, args);
								}
								finally {
									afterPrototypeCreation(beanName);
								}
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				/*(13)*/
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}
		if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
			try {
				return getTypeConverter().convertIfNecessary(bean, requiredType);
			}
			catch (TypeMismatchException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}

先說(shuō)下入?yún)ⅲ?/p>

  1. name:要查找的bean名,可以為raw name,也可以為alias name或者factoryBean name,Spring內(nèi)部會(huì)自行進(jìn)行轉(zhuǎn)換。

  2. requiredType:要返回的對(duì)象類型

  3. args:對(duì)象實(shí)例化時(shí)需要用到的構(gòu)造參數(shù)

  4. typeCheckOnly:該對(duì)象只是用來(lái)進(jìn)行類型檢查,而不會(huì)真正的進(jìn)行使用,可以避免實(shí)例化和初始化對(duì)象

具體過(guò)程為:

1.獲取raw name

?計(jì)算所給name對(duì)應(yīng)的內(nèi)部beanName,具體為循環(huán)去除name前面的&,再根據(jù)之前的介紹的,如果傳入的是別名,會(huì)查找到對(duì)應(yīng)的raw name

2.嘗試獲取bean實(shí)例

?使用上面獲得的beanName,調(diào)用內(nèi)部的getSingleton方法,獲取對(duì)應(yīng)的對(duì)象實(shí)例,賦值給sharedInstance。getSingleton方法來(lái)自于DefaultSingletonBeanRegistry,即這步嘗試直接從內(nèi)部維護(hù)的單例Map中獲取實(shí)例。這步可以檢測(cè)到手工注入的singleton,如第一節(jié)提到的ApplicationContext對(duì)象,就是Spring自己手動(dòng)注冊(cè)的。

3.bean實(shí)例已經(jīng)存在

?若sharedInstance不為空,且args參數(shù)為空,說(shuō)明該對(duì)象已經(jīng)存在,不需要再進(jìn)行實(shí)例化和初始化。由于在(1)的時(shí)候?qū)λ鶄鞯膎ame去除了&,需要判斷返回的對(duì)象是否符合要求。這時(shí)候,會(huì)使用getObjectForBeanInstance方法,對(duì)sharedInstance和name進(jìn)行判斷,返回對(duì)應(yīng)的實(shí)例,該方法主要內(nèi)容如下:

  1. 若name以&開(kāi)頭,但sharedInstance沒(méi)有實(shí)現(xiàn)FactoryBean接口,則拋出異常

  2. 若sharedInstance沒(méi)有實(shí)現(xiàn)FactoryBean接口,或者name以&開(kāi)頭,則直接將sharedInstance對(duì)象返回。即sharedInstace本身是從name對(duì)應(yīng)的FactoryBean獲取的對(duì)象。

  3. 若前面2個(gè)條件都不符合,則sharedInstance本身實(shí)現(xiàn)了FactoryBean接口,name也是以&開(kāi)頭,這時(shí)候會(huì)嘗試從FactoryBeanRegistrySupport中根據(jù)beanName(raw name)獲取已經(jīng)實(shí)例化的對(duì)象。若對(duì)象為空,即首次獲取,則將sharedInstace轉(zhuǎn)為FactoryBean,并調(diào)用該工廠方法獲取對(duì)象。這里涉及到FactoryBeanRegistrySupport的getObjectFromFactoryBean方法,該方法在使用FactoryBean獲得對(duì)象后,會(huì)調(diào)用上下文中已有的BeanPostProcessor對(duì)象列表,逐個(gè)執(zhí)行postProcessAfterInitialization方法,當(dāng)遇到處理后的結(jié)果為空,則直接返回,否則繼續(xù)遍歷執(zhí)行,如下,出現(xiàn)在AbstractAutowireCapableBeanFactory中:

Spring GetBean的使用流程

4.Bean實(shí)例不存在

?如果沒(méi)有找到beanName對(duì)應(yīng)的實(shí)例,即不存在對(duì)應(yīng)的單例實(shí)例,則轉(zhuǎn)入實(shí)例化該對(duì)象的流程,注意單例或者多例都需要實(shí)例化。

5.如果該beanName有對(duì)應(yīng)的在初始化中的多例對(duì)象,則拋出異常。

?AbstractBeanFactory內(nèi)部維護(hù)了一個(gè)ThreadLocal對(duì)象,用于維護(hù)當(dāng)前線程正在初始化的多例對(duì)象。

6.啟用雙親委托機(jī)制

?如果存在父容器,且父容器存在該beanName的定義,則委托給父容器完成。

7.如果本次調(diào)用不單是為了類型檢查,則標(biāo)記該beanName在創(chuàng)建中

?AbstractBeanFactory內(nèi)部維護(hù)了一個(gè)Set<String>集合alreadyCreated,用于存儲(chǔ)已經(jīng)創(chuàng)建好或者正在創(chuàng)建的bean

8.獲取該beanName對(duì)應(yīng)的BeanDefinition,包裝為RootBeanDefinition返回。

?AbstractBeanFactory內(nèi)部維護(hù)了一個(gè)Map<String, RootBeanDefinition>集合mergedBeanDefinitions,用于維護(hù)當(dāng)前已經(jīng)加載的各個(gè)bean定義bd。在加載該bean定義時(shí),如果存在父定義pdb,則會(huì)將pdb包裝為一個(gè)RootBeanDefinition,然后將當(dāng)前的bd覆蓋掉父定義的內(nèi)容,包括scope、lazyInit、dependsOn等屬性,達(dá)到繼承的效果。獲得RootBeanDefinition后,如果最后的定義中scope為空,則會(huì)默認(rèn)賦值為single。此外還有一個(gè)containingBd的概念,這個(gè)是相對(duì)于bd來(lái)說(shuō)的,指的是包含bd的外部bean定義,主要用于inner bean的情況。如果包含containingBd不為空,且不是單例,但是bd為單例,則bd的scope需要設(shè)置為containingBd的值,直白點(diǎn)說(shuō)就是包含被非單例bean包含的bean本身不能為單例(這段有點(diǎn)繞,還沒(méi)找到實(shí)際的例子,直接按照代碼里的直譯過(guò)來(lái))。

9.處理依賴的bean

?獲取該bean依賴的bean列表dependsOn值,對(duì)每個(gè)依賴的bean進(jìn)行逐一操作,先檢查該bean是否存在循環(huán)依賴,若不存在循環(huán)依賴,則將依賴關(guān)系緩存起來(lái),最后先實(shí)例化依賴的bean。其中檢查循環(huán)依賴很重要,如果沒(méi)有該步,最后實(shí)例化依賴的bean時(shí)會(huì)導(dǎo)致死循環(huán)。為此AbstractBeanFacotry內(nèi)部維護(hù)了兩個(gè)Map<String, Set<String>>屬性dependentBeanMap和dependenciesForBeanMap,分別用于緩存bean的依賴關(guān)系。前者表示bean從屬關(guān)系的緩存,緩存依賴于key所表示的bean的所有bean name,舉例來(lái)講,如果beanB的一個(gè)屬性是beanA,則beanA為key是被依賴方,beanB則為value是依賴方(從屬方)的一員;后者標(biāo)識(shí)bean依賴關(guān)系的緩存,緩存key所表示的bean依賴的所有bean name,舉例來(lái)講,如果beanB的一個(gè)屬性是beanA,則beanB是key從屬方,beanA則是value被依賴方的一員。如下為Spring檢查循環(huán)依賴的過(guò)程:

Spring GetBean的使用流程

?其中beanName為當(dāng)前bean,dependentBeanName為當(dāng)前bean所依賴的bean。大致過(guò)程為找出所有依賴beanName的bean列表transitiveDependency,遞歸判斷transitiveDependency是否也依賴dependentBeanNam,即如果 beanName依賴于 dependentBeanName ,而且 transitiveDependency依賴于 beanName, 如果transitiveDependency 依賴于dependentBeanName,即出現(xiàn)了環(huán),則存在循環(huán)依賴。

10.如果該bean為單例,則轉(zhuǎn)入初始化單例流程

?調(diào)用父類DefaultSingletonBeanRegistry的getSingleton模板方法,該模板方法會(huì)保證該單例只有被創(chuàng)建一次,創(chuàng)建完成后將對(duì)象緩存在內(nèi)部。真正實(shí)例化和初始化的過(guò)程在createBean方法中,其中如果該bean實(shí)例化失敗,則會(huì)調(diào)用destroySingleton方法進(jìn)行回收,這兩個(gè)方法在后面會(huì)進(jìn)行重點(diǎn)講解。同第二步類似,獲取該對(duì)象后,會(huì)再調(diào)用getObjectForBeanInstance檢查FactoryBean。

11.如果該bean為多例,則轉(zhuǎn)入初始化多例流程

?第(5)步講過(guò),內(nèi)部有一個(gè)ThreadLocal,保證多例在當(dāng)前線程創(chuàng)建時(shí)是唯一的,重點(diǎn)方法也是createBean。需要注意的是,如果是多例,創(chuàng)建失敗是不會(huì)進(jìn)行回收的。

12.如果該bean為其他scope,則轉(zhuǎn)入對(duì)應(yīng)的初始化流程

?具體過(guò)程同(10)一致,只是調(diào)用的模板委托給了具體的Scope對(duì)象。

13.初始化失敗,則清理相關(guān)內(nèi)容

?將該beanName從alreadyCreated移除,標(biāo)識(shí)該beanName還未創(chuàng)建。

二、createBean

?createBean方法主要用于完成bean的實(shí)例化和初始化過(guò)程,該方法在AbstractFactory中為抽象方法,具體實(shí)現(xiàn)是在AbstractAutowireCapableBeanFactory類中。如下為核心操作:

Spring GetBean的使用流程

1.resolveBeforeInstantiation

?創(chuàng)建對(duì)象前的代理口子,能夠攔截創(chuàng)建過(guò)程,使用自定義的代理對(duì)象來(lái)替換Spring內(nèi)部正常創(chuàng)建的對(duì)象,即上面判斷的,如果該方法返回對(duì)象不為空,則直接使用返回的對(duì)象返回。實(shí)現(xiàn)上, 會(huì)逐一遍歷所有的BeanPostProcessor,找出InstantiationAwareBeanPostProcessor對(duì)象,并執(zhí)行postProcessBeforeInstantiation方法,若返回結(jié)果不為空,則直接使用該方法返回,如下:

Spring GetBean的使用流程

?該方法主要用在AOP實(shí)現(xiàn)上,上節(jié)提到的CommonAnnotationBeanPostProcessor和PersistenceAnnotationBeanPostProcessor類雖然實(shí)現(xiàn)了該接口,但是postProcessBeforeInstantiation方法為空實(shí)現(xiàn)。

?若該方法返回對(duì)象不為空,則會(huì)逐一執(zhí)行BeanPostProcessor列表的postProcessAfterInitialization方法,以完成回調(diào)。

2.doCreateBean

?該方法的主要過(guò)程如下,省略了提前暴露bean實(shí)例的部分內(nèi)容。

Spring GetBean的使用流程

?由上圖可知,該過(guò)程完成了bean的實(shí)例化和初始化以及調(diào)用各回調(diào)接口的過(guò)程。具體為:

  1. 根據(jù)BeanDefinition實(shí)例化bean

?主要嘗試從各種方法進(jìn)行實(shí)例化,包括:

a. 使用工廠方法進(jìn)行實(shí)例化

b. 使用bean定義的構(gòu)造方法或者可用的構(gòu)造方法進(jìn)行實(shí)例化

c. 使用默認(rèn)的構(gòu)造方法進(jìn)行實(shí)例化

  1. 回調(diào)MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法

?如下,遍歷各個(gè)MergedBeanDefinitionPostProcessor實(shí)例,回調(diào)postProcessMergedBeanDefinition方法

Spring GetBean的使用流程

3.初始化對(duì)象,填充各屬性

?執(zhí)行初始化,實(shí)現(xiàn)屬性的依賴注入,在自動(dòng)進(jìn)行依賴注入前, 會(huì)先調(diào)用一個(gè)回調(diào)接口,以判斷是否需要自動(dòng)依賴注入,如下:

Spring GetBean的使用流程

?通過(guò)回調(diào)InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法來(lái)判斷。

?若需要進(jìn)行依賴注入,則會(huì)根據(jù)依賴策略:根據(jù)autowireByName或者autowireByType,為屬性字段找到符合定義的bean實(shí)例(會(huì)通過(guò)getBean方法調(diào)用)。在真正將值賦值給屬性前, 還會(huì)再次執(zhí)行回調(diào)接口,如下,回調(diào)InstantiationAwareBeanPostProcessor的postProcessPropertyValues方法,這里也可以進(jìn)行攔截。

Spring GetBean的使用流程

?若前面都沒(méi)被攔截到,則會(huì)真正將bean值復(fù)制給對(duì)應(yīng)的屬性,最終會(huì)通過(guò)反射設(shè)置field的accessable,然后將bean實(shí)例設(shè)置進(jìn)去。

4.執(zhí)行各回調(diào)接口

  1. 執(zhí)行Aware接口,包括BeanNameAware、BeanClassLoaderAware和BeanFactoryAware

  2. 執(zhí)行BeanPostProcessor的postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor類實(shí)現(xiàn)了該方法,用以回調(diào)@PostConstruct注解的方法,CommonAnnotationBeanPostProcessor繼承自該類,設(shè)置了initAnnotationType為PostConstruct.class)方法

  3. 如果該Bean實(shí)現(xiàn)了InitializingBean接口,則調(diào)用afterPropertiesSet方法

  4. 如果設(shè)置了init-method,則執(zhí)行init-method指定的方法

  5. 執(zhí)行BeanPostProcessor的postProcessAfterInitialization方法

5.判斷是否有銷毀接口,并添加到列表中

?如下,為處理過(guò)程,會(huì)先判斷當(dāng)前bean定義不是多例,且需要進(jìn)行銷毀回調(diào),才會(huì)進(jìn)行處理。如果是單例,則直接將其添加到響應(yīng)列表列表中進(jìn)行緩存,存儲(chǔ)在內(nèi)部維護(hù)的disposableBeans列表中;如果是其他socpe,則將其委托給對(duì)應(yīng)的Scope對(duì)象實(shí)現(xiàn)。

Spring GetBean的使用流程

?這里有幾個(gè)條件:

  1. 必須為非prototy

  2. 該bean存在銷毀方法,滿足一下條件之一即是

a. 該bean實(shí)現(xiàn)了DisposableBean接口

b. 該bean實(shí)現(xiàn)了AutoCloseable接口

c.該bean實(shí)現(xiàn)了Closeable接口

d.該bean定義的destory-method不為空

e.該bean符合DestructionAwareBeanPostProcessor.requiresDestruction方法的過(guò)濾條件

?只要符合以上條件,就會(huì)新建一個(gè)DisposableBeanAdapter對(duì)象進(jìn)行存儲(chǔ),并在銷毀時(shí)進(jìn)行相應(yīng)的接口回調(diào)。

三、回調(diào)接口順序

?結(jié)合之前幾節(jié)內(nèi)容,可以得到如下的回調(diào)順序:

Spring GetBean的使用流程

?以上為大致的過(guò)程,不含其它的回調(diào)接口,若有其它回調(diào)接口可以按照順序依次加入。

“Spring GetBean的使用流程”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI