• 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 属性的源码分析就到这里结束了.

      

     

  • 相关阅读:
    正经学C#_循环[do while,while,for]:[c#入门经典]
    Vs 控件错位 右侧资源管理器文件夹点击也不管用,显示异常
    asp.net core 获取当前请求的url
    在实体对象中访问导航属性里的属性值出现异常“There is already an open DataReader associated with this Command which must be
    用orchard core和asp.net core 3.0 快速搭建博客,解决iis 部署https无法登录后台问题
    System.Data.Entity.Core.EntityCommandExecution The data reader is incompatible with the specified
    初探Java设计模式3:行为型模式(策略,观察者等)
    MySQL教程77-CROSS JOIN 交叉连接
    MySQL教程76-HAVING 过滤分组
    MySQL教程75-使用GROUP BY分组查询
  • 原文地址:https://www.cnblogs.com/xiaomaomao/p/13965832.html
Copyright © 2020-2023  润新知