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

 

 

  

 

posted @   变体精灵  阅读(4665)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示