本文的內容是讀郝佳的《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();
}