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

有一種情況需要注意,name 屬性可以配置多個,用逗號分割,但是 id 屬性是不能配置多個的,例如:

<bean id="watermelon,w1,w2,w3" class="com.xiaomaomao.entity.Watermelon"> </bean>

這種情況下 id 屬性不會和 name 屬性一樣進行分割,這里的 id 屬性只有一個,beanName 就是 watermelon,w1,w2,w3

這樣的結果也正好印證了我們的配置.至此 bean 標簽的 Id 和 name 屬性的源碼分析就到這里結束了.

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM