一、前言

在 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 名称空间、默认的名称空间等等是什么意思,大家可以参考这篇博客: 

我们这里默认的名称空间是 xmlns="http://www.springframework.org/schema/beans" ,这个默认的名称空间里面有几个默认值,分别是 bean、alias、beans、description、import,这些标签会进入到 parseDefaultElement(ele, delegate) 这个分支中进行解析.

spring 复用的时候bean注入重复了 spring bean id重复_spring

除了这些默认值之外,我们还经常会在 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 ,读者可以参考博客: 
	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 的介绍 
	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 的情况下会抛异常
		// 大致的内容信息可以参考这篇博客 
		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 的集合

spring 复用的时候bean注入重复了 spring bean id重复_spring_02

如下是 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

spring 复用的时候bean注入重复了 spring bean id重复_命名空间_03

有一种情况需要注意,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 属性的源码分析就到这里结束了.