Spring Bean標籤屬性-id、name
一、前言
在 Spring 容器中每個 bean 物件都有一個唯一的名字 (beanName) 和 0 個或者多個別名 (aliases)
如果我們想從 IOC 容器中獲取 bean 物件,那麼我們可以通過 beanName 獲取,也可以通過別名獲取
beanFactory.getBean("beanName or alias");
下面我們就從原始碼的角度看一下我們平常在 bean 標籤中配置的 id、name ,以及我們上面說到的 beanName、aliases 它們這些到底是什麼,具體有什麼作用
我們這裡參照的原始碼是(4.3.11.RELEASE這個版本的)
由於Spring IOC 中的容器類和各自的方法眾多,我這裡只說一下對應的類、方法、以及程式碼的行號數
二、Spring 配置檔案:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="watermelon" class="com.xiaomaomao.entity.Watermelon"> <property name="name" value="西瓜"></property> <property name="color" value="原諒色"></property> <property name="price" value="3.0"></property> </bean> <bean class="com.xiaomaomao.entity.Banana"> <property name="name" value="香蕉"></property> <property name="color" value="黃色"></property> <property name="price" value="4.0"></property> </bean> <bean name="a1,a2,a3" class="com.xiaomaomao.entity.Apple"> <property name="name" value="蘋果"></property> <property name="color" value="紅色"></property> <property name="price" value="5.0"></property> </bean> <bean id="mango" name="m1,m2,m3" class="com.xiaomaomao.entity.Mango"> <property name="name" value="芒果"></property> <property name="color" value="黃色"></property> <property name="price" value="6.0"></property> </bean> </beans>
三、原始碼分析
Spring IOC 啟動到解析 bean 標籤前面的程式碼太多了,我這裡就不貼了,直接從解析 XML 這裡開始吧
// DefaultBeanDefinitionDocumentReader 類中的方法, 程式碼行號: 161 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // 判斷是否是根節點下面預設的名稱空間,我們這裡預設的名稱空間 就是 xmlns="http://www.springframework.org/schema/beans" // 也就是沒有 xnlns:字首的這個名稱空間 if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { // 解析預設的名稱空間下面預設的元素 parseDefaultElement(ele, delegate); } else { // 解析預設的名稱空間下面非預設的元素 delegate.parseCustomElement(ele); } } } } else { // 非預設的名稱空間下面的元素解析方式 delegate.parseCustomElement(root); } }
關於什麼是 XML 名稱空間、預設的名稱空間等等是什麼意思,大家可以參考這篇部落格:https://www.cnblogs.com/xiaomaomao/p/13968976.html
我們這裡預設的名稱空間是xmlns="http://www.springframework.org/schema/beans" ,這個預設的名稱空間裡面有幾個預設值,分別是 bean、alias、beans、description、import,這些標籤會進入到parseDefaultElement(ele, delegate) 這個分支中進行解析.
除了這些預設值之外,我們還經常會在 beans 標籤中定義 <mvc: />、<context: />、<aop: />等標籤,這些標籤就是預設名稱空間(http://www.springframework.org/schema/beans )裡面非預設的元素值,這些標籤會進到delegate.parseCustomElement(ele) 這個分支中進行解析.
言歸正傳,我們這裡要分析的是 <bean /> 標籤的解析過程,所以毫無疑問,應該進入到parseDefaultElement(ele, delegate) 這個分支中,點進去
// DefaultBeanDefinitionDocumentReader 類中的方法, 程式碼行號: 182
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 解析 import 標籤
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 解析 alias 標籤
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 解析 bean 標籤
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// 解析 beans 標籤
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 如果是 <beans> 裡面,巢狀 <beans> 標籤,需要遞迴處理
doRegisterBeanDefinitions(ele);
}
}
這裡面是對各個預設的標籤進行解析,找到 bean 標籤對應的解析邏輯,點進去
// DefaultBeanDefinitionDocumentReader 類中的方法,程式碼行號: 298
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 將<bean.../> 節點中的配置資訊提取出來,然後封裝到 BeanDefinitionHolder 物件中
// 這個物件封裝的資訊包括 BeanDefinition 物件、beanName、aliases(別名陣列)
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 註冊最終封裝的例項物件
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
// 異常處理
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 傳送註冊時間
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
首先看一下BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele) 這個方法,看看它是如何從 bean 標籤中提取出配置資訊的
在解讀這個方法之前,我們需要看一下 BeanDefinitionHolder 物件,瞭解一下它裡面具體包含的資訊是什麼.
public class BeanDefinitionHolder implements BeanMetadataElement {
// BeanDefinition 物件,這個很重要,我們平常所說的 bean 其實可以看做是一個 BeanDefinition 物件例項
private final BeanDefinition beanDefinition;
// bean 的名稱
private final String beanName;
// bean 的別名
private final String[] aliases;
...
}
這裡的 BeanDefinition 很重要,我們可以看一下
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
// Spring 預設的 scope 的值有 singleton 和 prototype
// 其實還有 request、session、globalSession 等,不過這些都是 web 的擴充套件
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
// 不重要
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
// 設定 parent bean, 這裡的 parent bean 和我們平常說的繼承關係的父類是不同的
// 這裡設定 parent bean,僅僅是為了繼承 parent bean 裡面的皮脂資訊而已
// 關於 parent bean ,讀者可以參考部落格: https://www.cnblogs.com/xiaomaomao/p/13960084.html
void setParentName(String parentName);
// 獲取 parent bean name
String getParentName();
// 設定 bean 的類名稱,將來是要通過反射來生成例項的
void setBeanClassName(String beanClassName);
// 獲取 bean 的類名稱
String getBeanClassName();
// 設定 bean 的 scope
void setScope(String scope);
String getScope();
// 設定是否懶載入
void setLazyInit(boolean lazyInit);
// 是否懶載入初始化
boolean isLazyInit();
// 設定該 bean 依賴的所有的 bean, 注意,這裡的依賴不是指的屬性依賴( 如 @ Autowire 標記的),是 depends-on 屬性設定的值
void setDependsOn(String... dependsOn);
String[] getDependsOn();
// 設定該 bean 是否能注入到其它的 bean 中
void setAutowireCandidate(boolean autowireCandidate);
boolean isAutowireCandidate();
同一介面的多個實現,如果不指定名字的話,Spring 會優先選擇設定 primary 為 true 的 bean
// primary:預設值為false,同一個介面的多個實現類物件,如果不指定名字的話, Spring 會優先選擇將 primary 設定為true 的 bean
void setPrimary(boolean primary);
boolean isPrimary();
// 如果該 bean 採用工廠方法生成,指定工廠的名稱
// Spring 中並不是所有的 bean 例項物件都是通過反射生成的,它們也可以通過工廠模式來生成
void setFactoryBeanName(String factoryBeanName);
String getFactoryBeanName();
// 工廠類中的工廠方法
void setFactoryMethodName(String factoryMethodName);
String getFactoryMethodName();
// 構造方法引數值
ConstructorArgumentValues getConstructorArgumentValues();
// bean 的屬性值,為 bean 做屬性注入的時候會用到
MutablePropertyValues getPropertyValues();
// 是否是單例物件
boolean isSingleton();
// 是否是多例物件
boolean isPrototype();
// 如果某個 bean 被設定為 abstract,那麼它是不能被例項化的它的作用僅僅是為了被其它的 bean 繼承用
// 可以參考部落格中對 abstract 的介紹 https://www.cnblogs.com/xiaomaomao/p/13960084.html
boolean isAbstract();
int getRole();
String getDescription();
String getResourceDescription();
BeanDefinition getOriginatingBeanDefinition();
}
我們從上面的介紹中可以知道 BeanDefinitonHolder 物件中包含三個屬性,BeanDefinition物件、beanName、aliases(別名陣列),而最重要的是 BeanDefiniton 物件,這個物件中包含了 <bean.../> 標籤中所有能配置的屬性,我們所謂的 bean ,其實就可以看做是一個 BeanDefinition 物件的例項.有了對這些知識的瞭解之後呢,我們繼續接著上面的程式碼來看,點開 parseBeanDefinitionElement(ele) 這個方法
// BeanDefinitionParserDelegate 類中的方法,程式碼行號: 437
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
// 獲取 bean 標籤中的 id、name 屬性的值,如果它們沒有配置,則它們的值為 ""
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 定義一個別名集合 aliases
List<String> aliases = new ArrayList<String>();
// 如果 bean 裡面配置了 name 屬性
if (StringUtils.hasLength(nameAttr)) {
// 將 nameAttr 這個字元進行分割,轉換成字串陣列(例如: nameArr="m1,m2,m3" ====> {m1,m2,m3}
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// 將陣列轉換成集合,並且新增到 aliases 這個 List 集合中
aliases.addAll(Arrays.asList(nameArr));
}
// 這裡的 id 就是我們 Bean 標籤中配置的 id 屬性,在 Spring 中 id 屬性代表的也就是 beanName
String beanName = id;
// 如果 bean 標籤中只配置了 name 屬性,沒有配置 id 屬性,那麼用別名列表的第一個名字作為 beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 將 name 屬性中的第一個值作為 beanName,剩下的作為別名(alias)注意這裡是 remove(0) 它會改變集合的長度的
// 例如 List <String> aliases = {a1,a2,a3} 經過 remove(0) 之後就變成了 {a2,a3}了,此時 beanName 為 a1,別名陣列為 {a2,a3}
beanName = aliases.remove(0);
// 如果日誌級別是 debug 級別的,會輸出下面這段日誌,但是 Spring 預設的日誌級別是 info的
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
// containingBean 是前面的方法傳過來的引數,值是 null
if (containingBean == null) {
// 校驗名字的唯一性(校驗現在使用的 beanName 是不是已經被載入過的 bean 使用了
checkNameUniqueness(beanName, aliases, ele);
}
// 根據 <bean....>....</bean> 標籤中的配置建立一個對應的 BeanDefinition 物件,
// 然後把配置中的資料都設定到 BeanDefinition 例項中去
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
// 如果 BeanDefiniton 例項物件不為空
if (beanDefinition != null) {
// 這裡如果 beanName 為空或者為 "",(也就是我們的 bean 標籤中既沒有配置 id 也沒有配置 name )
// 這裡 Spring 會按照框架定義的某種規則生成 beanName 和 aliases
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
// 如果我們不定義 id 和 name 屬性,Spring 會按照一定的規則生成 beanName 和 beanClassName,
// 如果 Spring 的配置檔案中只配合了一個 <bean class="com.xiaomaomao.spring.Watermelon"> 這樣的標籤
// 那麼生成的 beanName 和 beanClassName 的值如下:
// 1、beanName 為:com.xiaomaomao.spring.Watermelon#0
// 2、beanClassName 為:com.xiaomaomao.spring.Watermelon
beanName = this.readerContext.generateBeanName(beanDefinition);
// 獲取 beanClassName 的值
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
// 將 beanClassName 的值設定為別名,也就是 com.xiaomaomao.spring.Watermelon
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
// 將存放別名的 List<String> 集合,轉成別名陣列
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 將 BeanDefinition、beanName、aliasesArray 賦值給 BeanDefinitionHolder 物件
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
我們可以具體的看一下是怎麼解析 <bean>...</bean>標籤的,並如何將 bean 標籤中的的資料封裝到 BeanDefinition 物件中的(當然這個不是我們的重點)
// BeanDefinitionParserDelegate 類中的方法, 程式碼行號: 522
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {
// 把 beanName 的值賦值給本類的 ParseState 物件
this.parseState.push(new BeanEntry(beanName));
String className = null;
// 如果 bean 標籤中配置了 class 屬性,把 class 屬性配置的全包類名賦值給 className 這個變數
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
// 如果 bean 裡面配置了 parent 屬性,將 parent 屬性對應的值賦值給 parent 這個變數
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
// 建立 BeanDefinition 物件,並設定相應的屬性,裡面有設定 BeanClassName
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 設定 BeanDefinition 中定義的屬性,這些屬性定義在 AbstractBeanDefinition 中
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 下面是解析 bean 標籤中的子標籤
// 解析 <meta /> 標籤
parseMetaElements(ele, bd);
// 解析 <lookup-method /> 標籤
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析 <replaced-method /> 標籤
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析 <constructor-arg /> 標籤
parseConstructorArgElements(ele, bd);
// 解析 <property /> 標籤
parsePropertyElements(ele, bd);
// 解析 <qualifier /> 標籤
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
上面有說到如果我們沒有配置 id 和 name 屬性,那麼 Spring 框架幫我們生成的 beanName 和 aliases 分別是什麼呢?
// BeanDefinitionReaderUtils 工具類中的方法, 程式碼行號: 102
public static String generateBeanName(
BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
throws BeanDefinitionStoreException {
// 獲取 beanClassName ,前面執行 AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 的時候有設定 beanClassName
String generatedBeanName = definition.getBeanClassName();
// 如果存在父 Bean ,那麼 generatedBeanName 的值為父 bean 的名稱拼接 $child
// tips: 父 bean,不是我們說的繼承中的父子 bean,這裡指的是 bean 標籤中配置的 parent 屬性對應的 bean
if (generatedBeanName == null) {
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
}
// 如果是 FactoryBean ,那麼 generatedBeanName 的值為 FactoryBean 的名稱拼接 $created
else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName() + "$created";
}
}
// 如果不滿足上面三種情況,則丟擲異常
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
"'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
}
String id = generatedBeanName;
// 判斷是否是內部的 bean
if (isInnerBean) {
// 內部 bean 的 beanName 生成規則
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
}
else {
int counter = -1;
// 如果 counter = -1 或者是容器中已經存在了該 beanName 則一直執行迴圈
// 例如 Spring 的配置檔案中配置了一個 <bean class="com.xiaomaomao.entity.Banana">
// 那麼這裡的 id 值為 com.xiaomaomao.entity.Banana#0
// 如果配置檔案中配置了兩個相同的 <bean class="com.xiaomaomao.entity.Banana">
// 那麼第一個 beanName 為com.xiaomaomao.entity.Banana#0 ,第二個為 com.xiaomaomao.entity.Banana#1
while (counter == -1 || registry.containsBeanDefinition(id)) {
counter++;
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
}
}
return id;
}
那麼接著就是將上面程式碼裡生成的 id 賦值給了 beanName,前面設定的 beanClassName 賦值給了 aliases,通過上面的程式碼,我們最終將 beanName、aliases、BeanDefinition 物件使用 BeanDefinitionHolder 這個物件承載了全部的資料.
緊接著回到程式碼的入口位置
// DefaultBeanDefinitionDocumentReader 類中的方法,程式碼行號: 298
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 將<bean.../> 節點中的配置資訊提取出來,然後封裝到 BeanDefinitionHolder 物件中
// 這個物件封裝的資訊包括 BeanDefinition 物件、beanName、aliases(別名陣列)
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 如果有自定義的屬性,進行相應的解析
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 註冊最終封裝的例項物件
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
// 異常處理
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 傳送註冊時間
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
經過上面的分析,BeanDefinitionHolder 物件我們就創建出來了,並且該物件的三個屬性, BeanDefinition 物件、beanName、aliaese我們都分別給其賦了值.(注意,這裡的一個 bean 對應的是一個 BeanDefinitionHolder 物件,如果配置檔案中存在多個 bean 標籤,這裡會生成多個 BeanDefinitonHolder物件)
接著我們看一下BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());這個方法,看看是如何註冊 bean 的吧.
好了,我們回到 解析 bean 標籤的入口方法中,看一下怎麼註冊 bean 的吧
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 註冊 bean
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 註冊別名
// 如果還有別名的話,也要根據別名全部註冊一遍,不然根據別名就會找不到 Bean 了
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
// alias -> beanName 儲存它們的別名資訊,這個很簡單,用一個 map 儲存一下就可以了,
// 獲取的時候,會先將 alias 轉換為 beanName,然後再查詢
registry.registerAlias(beanName, alias);
}
}
}
看一下怎麼註冊 bean 的吧
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 校驗
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// oldBeanDefinition ,這裡涉及 allowBeanDefinitionOverriding 這個屬性,
// 如果兩個 bean 標籤配置了相同的 id 或者是 name ,那麼 Spring 中預設的就是允許後面註冊的 bean 覆蓋前面註冊的 bean
BeanDefinition oldBeanDefinition;
// 所有的 bean 的註冊最終都是放到 Map<String, BeanDefinition> beanDefinitionMap 這個 Map 集合中
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
// 如果存在重複名稱的 bean 的情況
if (oldBeanDefinition != null) {
// 如果 AllowBeanDefinitionOverriding 屬性的值是 false 的情況下會拋異常
// 大致的內容資訊可以參考這篇部落格 https://www.cnblogs.com/xiaomaomao/p/13928647.html
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
// 用 spring 定義的 bean 去覆蓋使用者自定義的 bean
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
// 如果兩個先後註冊的 bean 不同,那麼用後註冊的 bean 覆蓋前面註冊的 bean
else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
// 如果前後註冊的兩個 bean 內容相同
else {
// 如果日誌級別是 debug 級別的情況下,列印如下的日誌
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
// 使用後註冊的 bean 覆蓋前面註冊的 bean
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// 判斷是否已經有其它的 bean 開始初始化了
// 注意,註冊 bean 這個動作結束之後,bean 依然沒有被初始化,真正的初始化操作還在後面
// 在 spring 容器啟動的最後,會預初始化所有的單例 bean
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// 將 BeanDefinition 放到這個 map 中,這個 map 儲存了所有的 beanDefinition 例項物件
this.beanDefinitionMap.put(beanName, beanDefinition);
// beanDefinitionNames 是一個 List<String> 集合,裡面會按照 bean 標籤的配置順序儲存所有的 beanName,
this.beanDefinitionNames.add(beanName);
// 這是個 LinkedHashSet,代表的是手動註冊的 singleton bean,
// manualSingletonNames 是一個 Set<String> ,代表的是手動註冊的 singleton bean
// 注意這裡的是 remove() 方法,到這裡的 bean 當然不是手動註冊的
// 手動註冊指的是通過呼叫 registerSingleton(String beanName, Object singletonObject)方法註冊的 bean
// spring 會在後面手動註冊一些 bean ,例如 environment、systemProperties 等 bean,當然我們自己也可以在執行時註冊
// bean 到 spring 容器中
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
再看一下怎麼註冊 aliases(別名)
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
// 判斷 alias 和 beanName 是否相等,相等的話代表的是同一個,則移除重複的
// <bean id="watermelon" name="watermelon,w1,w2" class="com.xiaomaomao.entity.Watermelon">
// 那麼別名只有 w1 和 w2,雖然也配置了 watermelon 為別名,但是重複的會移除
if (alias.equals(name)) {
this.aliasMap.remove(alias);
}
else {
// 從 aliasMap 這個 Map 集合中獲取 別名所對應的值
// 這個 Map 的資料結構是 Map< alias,beanName>
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// 如果已經存在了該別名,直接返回,不需要再次註冊
return;
}
// 重複覆蓋
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
// 校驗別名迴圈
checkForAliasCircle(name, alias);
// 以別名為 key ,beanName 的值為 value ,存入 Map 集合
this.aliasMap.put(alias, name);
}
}
四、總結
<bean id="watermelon" class="com.xiaomaomao.entity.Watermelon"> </bean>
上面的配置代表的意思是 beanName 為 watermelon , 不存在別名
<bean class="com.xiaomaomao.entity.Banana"> </bean>
上面的配置代表的意思是 beanName 為 com.xiaomaomao.entity.Banana#0, 別名為com.xiaomaomao.entity.Banana
<bean name="a1,a2,a3" class="com.xiaomaomao.entity.Apple"> </bean>
上面的配置代表的意思是 beanName 為a1, 別名為 a2、a3
<bean id="mango" name="m1,m2,m3" class="com.xiaomaomao.entity.Mango"> </bean>
上面的配置代表的意思是 beanName 為mango, 別名為 m1、m2、m3
當我們解析完了 XML 配置檔案之後,可以在 DefaultListBeanFactory 這個類中找到 我們註冊的所有 beanNames 和 aliases
如下是所有的 beanName 的集合
如下是 alias 的集合,所有的別名都儲存在 aliasMap 這個 Map<alias,beanName> 中我們可以看到
beanName 為 mango 的 bean 對應的別名有 m1、m2、m3
beanName 為 a1 的 bean 對應的別名有 a2、a3
beanName 為 com.xiaomaomao.entity.Banana#0 對應的別名有 com.xiaomaomao.entity.Banana
這樣的結果也正好印證了我們的配置.至此 bean 標籤的 Id 和 name 屬性的原始碼分析就到這裡結束了.