bean标签的解析

前言

  之前的文章中提到过Spring中的标签包括默认和自定义两种标签,而两种标签的解析以及用法存在着很大的不同,接下来的文章将讲解默认标签的import、alias、bean、beans。这篇文章讲述的是bean标签。

默认标签

  默认的标签实在parseDefaultElement 函数中进行解析的,函数中的逻辑很清楚,分别对4种不同的标签做了不同的处理,如下:

 1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
 2         //对import标签的处理
 3         if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
 4             importBeanDefinitionResource(ele);
 5         }
 6         //对alias标签的处理
 7         else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
 8             processAliasRegistration(ele);
 9         }
10         //对bean标签的处理
11         else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
12             processBeanDefinition(ele, delegate);
13         }
14         //对beans标签的处理
15         else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
16             // recurse
17             doRegisterBeanDefinitions(ele);
18         }
19     }

bean标签的解析及注册

在4种标签中,bean标签的解析最重要而且最复杂,所以我们先从此标签开始深入分析,如果能理解此标签的解析过程,那么其他的标签也将会迎刃而解。首先我们来看看processBeanDefinition(ele,delegate)方法,对bean标签的解析进行一个大致上的流程了解:

 1 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
 2         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
 3         if (bdHolder != null) {
 4             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
 5             try {
 6                 // Register the final decorated instance.
 7                 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
 8             }
 9             catch (BeanDefinitionStoreException ex) {
10                 getReaderContext().error("Failed to register bean definition with name '" +
11                         bdHolder.getBeanName() + "'", ele, ex);
12             }
13             // Send registration event.
14             getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
15         }
16     }

对这段代码解释一下:

  (1)第2行:首先委托BeanDefinitionDelegate类的 parseBeanDefinitionElement()方法进行元素解析,返回BeanDefinitionHolder类的实例bdHolder,经过这个方法后,bdHolder实例已经包含我们配置的各种属性了,例如:class、name、id、alias之类的属性。

  (2)第4行:当返回的bdHolder不为空的情况下,若存在默认标签的子节点下还有自定义的属性,还需要再次对自定义标签进行解析。

  (3)第7行:解析完成后,需要对解析后的bdHolder进行注册,同样,注册委托给了BeanDefinitionReaderUtils的registerBeanDefinition()方法。

  (4)第14行:发出响应事件,通知相关的监听器,这个bean已经加载完成了。 

下面是bean标签解析的时序图,来加深理解:

解析BeanDefinition

上面我们对于bean标签的解析有一个大致的了解,接下来我们针对各个操作做具体的分析,首先我们从元素的解析及信息提取开始,也就是:

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

进入BeanDefinitionDelegate类的parseBeanDefinitionElement()方法:

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
        return parseBeanDefinitionElement(ele, null);
    }
 1 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
 2         //解析id属性
 3         String id = ele.getAttribute(ID_ATTRIBUTE);
 4         //解析name属性
 5         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
 6         //分割name属性
 7         List<String> aliases = new ArrayList<>();
 8         if (StringUtils.hasLength(nameAttr)) {
 9             // 将name属性的值通过,; 进行分割 转为字符串数字(即在配置文件中如配置多个name 在此做处理)
10             String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
11             aliases.addAll(Arrays.asList(nameArr));
12         }
13 
14         String beanName = id;
15         // 如果ID为空 使用配置的第一个name属性作为ID
16         if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
17             beanName = aliases.remove(0);
18             if (logger.isTraceEnabled()) {
19                 logger.trace("No XML 'id' specified - using '" + beanName +
20                         "' as bean name and " + aliases + " as aliases");
21             }
22         }
23 
24         if (containingBean == null) {
25             // 校验beanName和aliases的唯一性
26             // 内部核心为使用usedNames集合保存所有已经使用了的beanName和alisa
27             checkNameUniqueness(beanName, aliases, ele);
28         }
29 
30         // 进一步解析其他所有属性到GenericBeanDefinition对象中
31         AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
32         if (beanDefinition != null) {
33             //如果不存在beanName那么根据Spring中提供的命名规则为当前bean生成对应的beanName
34             if (!StringUtils.hasText(beanName)) {
35                 try {
36                     if (containingBean != null) {
37                         beanName = BeanDefinitionReaderUtils.generateBeanName(
38                                 beanDefinition, this.readerContext.getRegistry(), true);
39                     }
40                     else {
41                         beanName = this.readerContext.generateBeanName(beanDefinition);
42                         // Register an alias for the plain bean class name, if still possible,
43                         // if the generator returned the class name plus a suffix.
44                         // This is expected for Spring 1.2/2.0 backwards compatibility.
45                         String beanClassName = beanDefinition.getBeanClassName();
46                         if (beanClassName != null &&
47                                 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
48                                 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
49                             aliases.add(beanClassName);
50                         }
51                     }
52                     if (logger.isTraceEnabled()) {
53                         logger.trace("Neither XML 'id' nor 'name' specified - " +
54                                 "using generated bean name [" + beanName + "]");
55                     }
56                 }
57                 catch (Exception ex) {
58                     error(ex.getMessage(), ele);
59                     return null;
60                 }
61             }
62             String[] aliasesArray = StringUtils.toStringArray(aliases);
63             // 将信息封装到BeanDefinitionHolder对象中
64             return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
65         }
66 
67         return null;
68     }

上述代码便是对默认标签解析的全过程了。当然了,对Spring的解析需要像洋葱剥皮一样,一层一层的进行。尽管现在只能看到对id以及name属性的解析,但是思路我们已经了解了,在对属性展开解析前,Spring在外层又做了一个当前层的功能架构,在当前层完成的主要工作如下:

  (1)第3~28行:提取元素中的id以及name属性。

  (2)第31行:进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中。

  (3)第32~61行:如果检测到Bean没有指定beanName,那么使用默认规则为此Bean生成beanName。

  (4)第64行:将获取到的信息封装到BeanDefinitionHolder的实例中。

来进一步查看步骤2,中对标签其他属性的解析过程:

public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, @Nullable BeanDefinition containingBean) {

        this.parseState.push(new BeanEntry(beanName));

        String className = null;
        //解析class属性
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }
        String parent = null;
        //解析parent属性
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }

        try {
            //创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            //硬编码解析默认bean的各种属性
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            //提取description
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
            //解析元数据
            parseMetaElements(ele, bd);
            //解析lookup-Method属性
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            //解析replaced-method属性
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

            //解析构造函数参数
            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;
    }

 上述代码解析了bean标签的所有属性,不论是常用的还是不常用的,但是还是有一些复杂的属性需要进一步的解析,接下来,我们继续了解这些复杂的标签属性的解析:

 创建用于属性承载的BeanDefinition

  BeanDefinition是一个接口,在Spring中有三个对它的实现:RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition。这三种实现均继承了AbstractBeanDefinition,其中BeanDefinition是配置文件<bean>元素标签在容器中的内部表示形式。<bean>元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、scope、lazyInit属性,BeanDefinition和<bean>中的属性是一一对应的。其中RootBeanDefinition是最常用的实现类,它对应一般性的<bean>元素标签,GenericBeanDefinition是自2.5版本以后新加入的bean文件配置属性定义类,是一站式服务类。

  在配置文件中可以定义父<bean>和子<bean>,父<bean>用RootBeanDefinition表示,而子<bean>用ChildBeanDefinition表示,而没有父<bean>的<bean>就使用RootBeanDefinition表示。AbstractBeanDefinition对两者共同的类信息进行抽象。

  Spring通过BeanDefinition将配置文件中的<bean>配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。它们之间的关系如下:

由上图可以看出,要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例,而上述代码createBeanDefinition(className,parent)的作用就是实现此功能。

protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
            throws ClassNotFoundException {

        return BeanDefinitionReaderUtils.createBeanDefinition(
                parentName, className, this.readerContext.getBeanClassLoader());
    }

跟踪代码下去:BeanDefinitionReaderUtils类:

public static AbstractBeanDefinition createBeanDefinition(
            @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

        GenericBeanDefinition bd = new GenericBeanDefinition();
        // parentName可能为空
        bd.setParentName(parentName);
        if (className != null) {
            // 如果classLoader不为空
            // 则使用传入的classLoader同一虚拟机加载类对象 否则只记录classLoader
            if (classLoader != null) {
                bd.setBeanClass(ClassUtils.forName(className, classLoader));
            }
            else {
                bd.setBeanClassName(className);
            }
        }
        return bd;
    }

至此 createBeanDefinition() 已经说完了,而且我们也获得了用于承载属性的AbstractBeanDefinition了。

解析各种属性

  上述已经了解了创建bean信息的承载实例,接下来便可以进行bean信息的各种属性解析了,首先我们进入parseBeanDefinitionAttributes方法,此方法是对Element所有元素属性进行解析:

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
            @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

        //scope和singleton两种属性只能指定其中之一,不可以同时出现,否则Spring会抛出异常
        if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
            error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
        }
        //解析scope属性
        else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
            bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
        }
        else if (containingBean != null) {
            // 在嵌入BeanDefinition情况下且没有单独指定scope属性则使用父类默认的属性
            bd.setScope(containingBean.getScope());
        }
        //解析abstract属性
        if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
            bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
        }
        //解析lazy-init属性
        String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
        if (DEFAULT_VALUE.equals(lazyInit)) {
            lazyInit = this.defaults.getLazyInit();
        }
        //若没有设置或者设置成其他字符都会被设置为false
        bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
        //解析autowire属性
        String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
        bd.setAutowireMode(getAutowireMode(autowire));
        //解析depends-on属性
        if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
            String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
            bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
        }
        //解析autowire-candidate属性
        String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
        if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) {
            String candidatePattern = this.defaults.getAutowireCandidates();
            if (candidatePattern != null) {
                String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
                bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
            }
        }
        else {
            bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
        }
        //解析primary属性
        if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
            bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
        }
        //解析init-method属性
        if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
            String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
            bd.setInitMethodName(initMethodName);
        }
        else if (this.defaults.getInitMethod() != null) {
            bd.setInitMethodName(this.defaults.getInitMethod());
            bd.setEnforceInitMethod(false);
        }
        //解析destroy-method属性
        if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
            String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
            bd.setDestroyMethodName(destroyMethodName);
        }
        else if (this.defaults.getDestroyMethod() != null) {
            bd.setDestroyMethodName(this.defaults.getDestroyMethod());
            bd.setEnforceDestroyMethod(false);
        }
        //解析factory-method属性
        if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
            bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
        }
        //解析factory-bean属性
        if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
            bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
        }

        return bd;
    }

 从上述代码中可以清楚的看出Spring完成了对所有bean属性的解析,这些属性中有很多是我们经常使用到的,当然也有一些是不熟悉甚至都没有使用过的,有兴趣的可以继续追踪源码了解每一个属性。

在完成上一步,也就是对bean所有属性的解析后,将会把获取到的信息都封装到BeanDefinitionHolder中,来看看这个类的构造函数的源码:

public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName, @Nullable String[] aliases) {
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");
        Assert.notNull(beanName, "Bean name must not be null");
        this.beanDefinition = beanDefinition;
        this.beanName = beanName;
        this.aliases = aliases;
    }

可以看出也就跟普通的构造函数一样,对相应的属性赋值,构造一个BeanDefinitionHolder的实例。

以上,就是对于bean标签的解析。

参考:《Spring源码深度解析》 郝佳 编著:

posted on 2018-12-06 21:48  AoTuDeMan  阅读(1125)  评论(0编辑  收藏  举报

导航