Spring源碼2 Bean標籤的解析

本文的內容是讀郝佳的《Spring源碼深度解析》整理的筆記。

Bean的屬性和子元素

屬性: 可以和bean寫在一個尖括號<>裏面的,例如:下面bean標籤中的class就是bean的屬性。

<bean id="myTestBean" class="MyBean"/>

子元素: 寫在<bean>和</bean>之間的標籤元素,例如下面例子中meta就是bean的子元素。

<bean id="myTestBean" class="MyBean">
    <meta key="description" value="這是一個神奇的網站!"/>
</bean>

Bean常用屬性

  • scope:默認是singleton,可選值爲prototype、request、session、application、websocket。singleton是指一個Spring容器中,一個Bean定義只有一個對象實例;prototype允許Bean的定義可以被實例化多次,每次調用都創建一個實例;request在一次HTTP請求中,每個Bean定義對應一個實例,session表示在在土匪session中,每個Bean定義對應一個實例,request和session的作用域僅在基於Web的Spring上下文(例如:SpringMVC)中才有效。
  • abstract:默認爲false,表示當前bean是一個抽象的bean,從而不會爲它生成實例化對象。一般是用來聲明抽象bean,將一些公共的屬性放到一塊,這樣就能減少重複的代碼。
  • lazy-init:默認爲false,立即加載;true爲用到時再加載。(lazy-init 設置只對scop屬性爲singleton的bean起作用)
  • autowire:默認爲No,可選值爲byName、byType、constructor、default。No表示不啓用自動裝配,Autowire默認的值;byName表示通過屬性的名字的方式查找JavaBean依賴的對象併爲其注入。比如說類Computer有個屬性printer,指定其autowire屬性爲byName後,Spring IoC容器會在配置文件中查找id/name屬性爲printer的bean,然後使用Seter方法爲其注入;byType表示通過屬性的類型查找JavaBean依賴的對象併爲其注入。比如類Computer有個屬性printer,類型爲Printer,那麼,指定其autowire屬性爲byType後,Spring IoC容器會查找Class屬性爲Printer的bean,使用Seter方法爲其注入;constructor與byType一樣,也是通過類型查找依賴對象。與byType的區別在於它不是使用Seter方法注入,而是使用構造子注入。default表示由上級標籤的default-autowire屬性確定。
  • dependency-check:默認爲none,可選值爲simple、object、all。dependency-check=“simple” 就說 只檢查簡單類型屬性以及集合類型屬性 dependency-check=“objects” 就說 檢查除簡單類型屬性以及集合類型屬性外的引用類型屬性 ,當然,all就是檢查所有的 setter 。 所有的setter 方法對應的 屬性, 必須在bean 的子元素property 中進行配置。
  • depends-on:只是表明依賴關係(不一定會引用),這個依賴關係決定了被依賴的bean必定會在依賴bean之前被實例化,反過來,容器關閉時,依賴bean會在被依賴的bean之前被銷燬。
  • autowire-candidate:默認爲true,設置爲false,容器在查找自動裝配對象時,將不考慮該bean,即該bean不會被作爲其它bean自動裝配的候選者,但該bean本身還是可以使用自動裝配來注入其它bean的。
  • primary:默認爲false,當使用@Autowired想要注入一個這個類型的bean時,就不會因爲容器中存在多個該類型的bean而出現異常。而是優先使用primary爲true的bean。
  • init-method:配置一個類的初始化方法,類完整的實例被創建出來後,才能走初始化方法。
  • destory-method:容器銷燬之前所調用的方法。
  • factory-bean:實例化工廠類。
  • factory-method:調用工廠方法。

Bean常用子元素

  • meta:元數據。當需要使用裏面的信息時可以通過key獲取。通過BeanDefinition 的getAttribute方法獲取。
  • lookup-method:當有一個父類A,兩個繼承A的兩個子類分別B、C,在某個bean下有一個方法getA,getA的返回值類型的A類型,可以在這個bean的配置下面指定getA返回的實例是B或者C。
  • replaced-method:可以替換bean中的某一個方法,通過實現MethodReplacer聲明一個類,將要替換的方法聲明在該類下,然後在bean的配置下用該子元素指定哪一個方法要被替換。
  • constructor-arg:可以通過這個子元素給bean的構造方法傳參數。index用來指定是第幾個參數,type用來指定參數類型,name可以配置參數名字,與真實構造方法中的參數名字一樣,ref如果參數類型是引用類型,用ref指定傳入哪個bean的實例。
  • property:給bean的屬性賦值。
  • qualifier:當一個A類有兩個子類B和C時,如果一個bean被定義爲A類型,spring會不知道該注入B和C中的哪一個,用qualifier指定注入哪一個。

Bean標籤的解析

1.繼續parseDefaultElement(ele, delegate);方法的跟進,該方法主要實現的是默認標籤的解析。
在這裏插入圖片描述
2.進入該方法以後,先關注Bean相關標籤的解析,也就是方法processBeanDefinition
在這裏插入圖片描述
3.繼續往下走,進入processBeanDefinition方法中,該方法中首先利用parseBeanDefinitionElement方法將配置文件中關於Bean的信息封裝到BeanDefinitionHolder類中。這裏簡單介紹一下BeanDefinitionHolder類(如下圖),該類裏面有3個屬性,分別是真正的BeanDefinition,以及beanName的名字,和該bean的別名數組;其實有關bean的真正信息是存儲在BeanDefinition類型的beanDefinition屬性中。BeanDefinitionHolder主要是處理bean別名與bean之間的映射。

在這裏插入圖片描述在這裏插入圖片描述
4.繼續跟進parseBeanDefinitionElement方法:
在這裏插入圖片描述
5.繼續跟進parseBeanDefinitionElement方法,該方法主要是對前面提到Bean的默認標籤進行解析,依次是:description、meta、lookup-method、replaced-method、constructor-arg、property、qualifier標籤。
在這裏插入圖片描述
6.到這裏爲止,BeanDefinition中的信息已經裝填,下面回到第3步中的decorateBeanDefinitionIfRequired方法中該方法,這個方法是對BeanDefinition進行裝飾,這句話其實是針對Bean標籤中的自定義標籤進行解析,適用於這樣的配置:

    <bean id="myTestBean" class="MyBean">
        <mybean:user username="aa"/>
    </bean>

該方法的流程主要爲:首先獲取屬性或者元素的命名空間,以此來判斷該元素或者屬性是否適用於自的定義標籤解析條件,找出自定義類型所對應的NamespaceHandler並進行進一步解析。
在這裏插入圖片描述

7.註冊解析的BeanDefinition,也就是registerBeanDefinition方法。
在這裏插入圖片描述
8.跟進registerBeanDefinition方法,這個方法分別利用beanName和bean的別名對bean進行註冊。
在這裏插入圖片描述
9.進入registerBeanDefinition方法中:
在這裏插入圖片描述
繼續上面方法:在這裏插入圖片描述
10.繼續第8步中的registry.registerAlias(beanName, alias);方法進入:
在這裏插入圖片描述

補充部前面提到的部分方法

校驗bean中重載的validate方法((AbstractBeanDefinition) beanDefinition).validate();
在這裏插入圖片描述
上面涉及到的方法:

  • hasMethodOverrides()
	public boolean hasMethodOverrides() {
		return (this.methodOverrides != null && !this.methodOverrides.isEmpty());
	}
  • getFactoryMethodName()
	public String getFactoryMethodName() {
		return this.factoryMethodName;
	}
  • hasBeanClass()
	public boolean hasBeanClass() {
		return (this.beanClass instanceof Class);
	}
  • prepareMethodOverrides()
	/**
	 * Validate and prepare the method overrides defined for this bean.
	 * Checks for existence of a method with the specified name.
	 * @throws BeanDefinitionValidationException in case of validation failure
	 */
	public void prepareMethodOverrides() throws BeanDefinitionValidationException {
		// Check that lookup methods exists.
		if (hasMethodOverrides()) {
			Set<MethodOverride> overrides = getMethodOverrides().getOverrides();
			synchronized (overrides) {
				for (MethodOverride mo : overrides) {
					prepareMethodOverride(mo);
				}
			}
		}
	}
  • prepareMethodOverrides()中涉及到的prepareMethodOverride(mo)
	protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
		int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
		if (count == 0) {
			throw new BeanDefinitionValidationException(
					"Invalid method override: no method with name '" + mo.getMethodName() +
					"' on class [" + getBeanClassName() + "]");
		}
		else if (count == 1) {
			// Mark override as not overloaded, to avoid the overhead of arg type checking.
			mo.setOverloaded(false);
		}
	}
  • resetBeanDefinition(beanName)方法:
	protected void resetBeanDefinition(String beanName) {
		// Remove the merged bean definition for the given bean, if already created.
		clearMergedBeanDefinition(beanName);

		// Remove corresponding bean from singleton cache, if any. Shouldn't usually
		// be necessary, rather just meant for overriding a context's default beans
		// (e.g. the default StaticMessageSource in a StaticApplicationContext).
		destroySingleton(beanName);

		// Reset all bean definitions that have the given bean as parent (recursively).
		for (String bdName : this.beanDefinitionNames) {
			if (!beanName.equals(bdName)) {
				BeanDefinition bd = this.beanDefinitionMap.get(bdName);
				if (beanName.equals(bd.getParentName())) {
					resetBeanDefinition(bdName);
				}
			}
		}
	}
  • hasBeanCreationStarted()方法:
	protected boolean hasBeanCreationStarted() {
		return !this.alreadyCreated.isEmpty();
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章