zoukankan      html  css  js  c++  java
  • 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源码深度解析》 郝佳 编著:

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    偶串_牛客网
    制造回文_牛客网
    字典树(前缀树)的实现
    动态规划LeetCode174地下城游戏
    动态规划LeetCode64最小路径和
    动态规划LeetCode300最长上升子序列
    动态规划LeetCode120三角形最小路径和
    Zabbix 监控sqlserver
    如何回收VCSA 6自带的vPostgres数据库空间
    领益科技:导出Wireless组中的成员
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/10073647.html
Copyright © 2011-2022 走看看