Spring Bean 标签 id 和 name 属性
一、前言
在 Spring 容器中每个 bean 对象都有一个唯一的名字 (beanName) 和 0 个或者多个别名 (aliases)
如果我们想从 IOC 容器中获取 bean 对象,那么我们可以通过 beanName 获取,也可以通过别名获取
1 | beanFactory.getBean( "beanName or alias" ); |
下面我们就从源码的角度看一下我们平常在 bean 标签中配置的 id、name ,以及我们上面说到的 beanName、aliases 它们这些到底是什么,具体有什么作用
我们这里参照的源码是( 4.3.11.RELEASE这个版本的 )
由于Spring IOC 中的容器类和各自的方法众多,我这里只说一下对应的类、方法、以及代码的行号数
二、Spring 配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?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 这里开始吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // 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) 这个分支中,点进去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 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 标签对应的解析逻辑,点进去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // 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 对象,了解一下它里面具体包含的信息是什么.
1 2 3 4 5 6 7 8 9 10 | public class BeanDefinitionHolder implements BeanMetadataElement { // BeanDefinition 对象,这个很重要,我们平常所说的 bean 其实可以看做是一个 BeanDefinition 对象实例 private final BeanDefinition beanDefinition; // bean 的名称 private final String beanName; // bean 的别名 private final String[] aliases; ... } |
这里的 BeanDefinition 很重要,我们可以看一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | 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) 这个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | // 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 对象中的(当然这个不是我们的重点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | // 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 分别是什么呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | // 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 这个对象承载了全部的数据.
紧接着回到代码的入口位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // 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 的吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 的吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | 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(别名)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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); } } |
四、总结
1 | <bean id= "watermelon" class = "com.xiaomaomao.entity.Watermelon" > </bean> |
上面的配置代表的意思是 beanName 为 watermelon , 不存在别名
1 | <bean class = "com.xiaomaomao.entity.Banana" > </bean> |
上面的配置代表的意思是 beanName 为 com.xiaomaomao.entity.Banana#0, 别名为 com.xiaomaomao.entity.Banana
1 | <bean name= "a1,a2,a3" class = "com.xiaomaomao.entity.Apple" > </bean> |
上面的配置代表的意思是 beanName 为 a1, 别名为 a2、a3
1 | <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 属性是不能配置多个的,例如:
1 | <bean id= "watermelon,w1,w2,w3" class = "com.xiaomaomao.entity.Watermelon" > </bean> |
这种情况下 id 属性不会和 name 属性一样进行分割,这里的 id 属性只有一个,beanName 就是 watermelon,w1,w2,w3
这样的结果也正好印证了我们的配置.至此 bean 标签的 Id 和 name 属性的源码分析就到这里结束了.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?