zoukankan      html  css  js  c++  java
  • 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 属性的源码分析就到这里结束了.

      

     

  • 相关阅读:
    TeeChart的X轴,使用伪装的时间
    线程池
    修练8年C++面向对象程序设计之体会
    使用RESTClient插件数据模拟(GET,POST)提交
    :施密特建议尾随年轻的专业人士了解技术公司
    社会保障系列1《介绍》
    Centos根据系统VPS安装SendMail组件使WordPress支持E-mail
    Codeforces Round #107 (Div. 2)---A. Soft Drinking
    [Unity3D]Unity3D游戏开发3D选择场景中的对象,并显示轮廓效果强化版
    怎么样ubuntu 64 11.04 在执行32位程序
  • 原文地址:https://www.cnblogs.com/xiaomaomao/p/13965832.html
Copyright © 2011-2022 走看看