上一節(jié)分析了Spring解析xml默認(rèn)命名空間并將解析到的數(shù)據(jù)封住至BeanDefinitionHolder對(duì)象中。接下來(lái)就可以執(zhí)行BeanDefinition的注冊(cè)了闷愤。該過(guò)程分為兩個(gè)部分,注冊(cè)BeanDefinition和注冊(cè)aliases(別名)何暇。
1. 引
上一節(jié)分析到了processBeanDefinition方法中的第一步句柠,接著往下執(zhí)行就應(yīng)該是裝飾BeanDefinition對(duì)象和執(zhí)行BeanDefinition注冊(cè)。
/**
* 解析bean標(biāo)簽將其轉(zhuǎn)換為definition并注冊(cè)到BeanDefinitionRegistry
* Process the given bean element, parsing the bean definition and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 1桃序、將解析的節(jié)點(diǎn)信息封裝至BeanDefinitionHolder對(duì)象
// BeanDefinitionHolder-->封裝了BeanDefinition,beanName以及aliases
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 2杖虾、裝飾BeanDefinition
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 3、執(zhí)行注冊(cè)
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 4媒熊、發(fā)送注冊(cè)事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
1. 注冊(cè)BeanDefinition步驟簡(jiǎn)析
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
// Register bean definition under primary name.
// 1奇适、注冊(cè)BeanDefinition
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
// 2、注冊(cè)aliases(別名)
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
從代碼中可以了解到芦鳍,該過(guò)程一共分為了兩個(gè)部分
- 1嚷往、注冊(cè)BeanDefinition
- 2、注冊(cè)aliases(別名)
下面對(duì)這兩個(gè)過(guò)程逐一進(jìn)行分析柠衅。
3.注冊(cè)注冊(cè)BeanDefinition
注冊(cè)注冊(cè)BeanDefinition的工作交給了BeanDefinitionRegistry接口的默認(rèn)實(shí)現(xiàn)DefaultListableBeanFactory皮仁。該過(guò)程還是比較復(fù)雜的。來(lái)看代碼菲宴。贷祈。。
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
// 1喝峦、如果beanDefinition是AbstractBeanDefinition實(shí)例,則驗(yàn)證
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
//驗(yàn)證不能將靜態(tài)工廠方法與方法重寫(xiě)相結(jié)合(靜態(tài)工廠方法必須創(chuàng)建實(shí)例)
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex);
}
}
// 2势誊、優(yōu)先嘗試從緩存中加載BeanDefinition
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
// beanName已經(jīng)存在且不允許被覆蓋,拋出異常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
// 使用新的BeanDefinition覆蓋已經(jīng)加載的BeanDefinition谣蠢,if else中只有日志打印键科,無(wú)實(shí)質(zhì)代碼闻丑,刪除為了閱讀方便
else if (existingDefinition.getRole() < beanDefinition.getRole()) {}
else if (!beanDefinition.equals(existingDefinition)) {}
else {}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// 3、緩存中無(wú)對(duì)應(yīng)的BeanDefinition勋颖,則直接注冊(cè)
else {
// 如果beanDefinition已經(jīng)被標(biāo)記為創(chuàng)建(為了解決單例bean的循環(huán)依賴(lài)問(wèn)題)
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
// 加入beanDefinitionMap
this.beanDefinitionMap.put(beanName, beanDefinition);
// 創(chuàng)建List<String>并將緩存的beanDefinitionNames和新解析的beanName加入集合
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
// 將updatedDefinitions賦值給beanDefinitionNames
this.beanDefinitionNames = updatedDefinitions;
// 如果manualSingletonNames中包含新注冊(cè)的beanName
if (this.manualSingletonNames.contains(beanName)) {
// 創(chuàng)建set集合并將manualSingletonNames加入到新創(chuàng)建的set集合
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
// 移除新注冊(cè)的beanName
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
//以上操作就是重新創(chuàng)建beanDefinitionNames和manualSingletonNames集合,這段代碼我也不是很理解...
}
else {
// 將beanDefinition信息維護(hù)至緩存
// beanDefinitionMap-->(key->beanName,value->beanDefinition)
this.beanDefinitionMap.put(beanName, beanDefinition);
// beanDefinitionNames-->維護(hù)了beanName集合
this.beanDefinitionNames.add(beanName);
// manualSingletonNames緩存了手動(dòng)注冊(cè)的單例bean嗦嗡,所以需要調(diào)用一下remove方法,防止beanName重復(fù)
// 例如:xmlBeanFactory.registerSingleton("myDog", new Dog());
// 就可以向manualSingletonNames中注冊(cè)單例bean
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// 4饭玲、重置BeanDefinition侥祭,
// 當(dāng)前注冊(cè)的bean的定義已經(jīng)在beanDefinitionMap緩存中存在,
// 或者其實(shí)例已經(jīng)存在于單例bean的緩存中
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
注冊(cè)流程在代碼里已經(jīng)標(biāo)注的很清楚了茄厘,其中synchronized方法中的代碼矮冬,分析不是很明確,希望了解的同學(xué)可以留言指正次哈。
4.注冊(cè)別名
// 注冊(cè)別名
@Override
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized (this.aliasMap) {
// 如果beanName與別名相同
if (alias.equals(name)) {
//移除別名
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
else {
//嘗試從緩存中獲取別名
String registeredName = this.aliasMap.get(alias);
//如果別名已經(jīng)在緩存中存在
if (registeredName != null) {
//緩存中的別名和beanName(注意:不是別名)相同,不做任何操作,不需要再次注冊(cè)
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
//緩存中存在別名,且不允許覆蓋,拋出異常
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
//檢查給定名稱(chēng)是否已指向另一個(gè)方向的別名作為別名,預(yù)先捕獲循環(huán)引用并拋出相應(yīng)的IllegalStateException
checkForAliasCircle(name, alias);
//緩存別名
this.aliasMap.put(alias, name);
}
}
}
5.分析
以上操作已經(jīng)完成了整個(gè)BeanDefinition和別名的注冊(cè)胎署,加上以前分析的代碼我們可以發(fā)現(xiàn)一些Spring源碼中的書(shū)寫(xiě)技巧。
- 方法職責(zé)單一窑滞,Spring中很少有一個(gè)長(zhǎng)而復(fù)雜的方法琼牧,也很少在一個(gè)方法里處理多個(gè)邏輯,即便有也是將具體的邏輯分支細(xì)化哀卫,在主方法里調(diào)用巨坊。
- 處理流程明確簡(jiǎn)析,Spring中很多方法此改,例如我們今天分析的BeanDefinition注冊(cè)趾撵,以及接下來(lái)要分析的獲取bean的過(guò)程等,都是先進(jìn)行合法驗(yàn)證共啃、嘗試從緩存中獲取占调、如未能從緩存中獲取到才去真正創(chuàng)建對(duì)象實(shí)例、將新創(chuàng)建的實(shí)例加入緩存等等移剪。
以上只是兩個(gè)小例子妈候,多讀代碼,多跟蹤調(diào)試挂滓,才是閱讀開(kāi)源代碼的途徑苦银,隨著閱讀次數(shù)的增加,相信大家會(huì)原來(lái)越加深對(duì)框架的理解赶站,當(dāng)我們真正能讀懂Spring的源碼之后幔虏,相信大家在看其他的源碼,也能觸類(lèi)旁通贝椿,成為閱讀開(kāi)源代碼的高手想括。