• Spring读书笔记——bean解析


    前情回顾

    上篇《Spring读书笔记——bean加载》我们从代码角度介绍了有哪些类负责解析XML文件,又是如何一步步从XML格式脱变成我们熟悉的bean的,直到DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法。我们抽丝剥茧,终于快看到Spring是如何解析bean的代码了。
    在此之前,我们回一下上篇看到过的主要类和方法
    XmlBeanFactory(XmlFactory) ->
    XmlBeanDefinitionReader(loadBeanDefinitions)->
    XmlBeanDefinitionReader(doLoadBeanDefinitions)->
    XmlBeanDefintionReader(registerBeanDefinitions)
    DefaultBeanDefinitionDocumentReader(registerBeanDefinitions)->
    DefaultBeanDefinitonDocumentReader(doRegisterBeanDefinitions)

    解析的类型

    Spring的标签分为默认标签和自定义标签两种。
    默认标签主要包括:import、alias、bean和beans
    自定义标签:顾名思义就是自定义的标签,比如

    DefaultBeanDefinitionDocumentReader

    从XmlBeanDefinitionReader的registerBeanDefinitions开始,我们第一步要做的就是创建一个Xml文档的解析类

    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    

    createBeanDefinitionDocumentReader方法创建了类DefaultBeanDefinitionDocumentReader。
    从DefaultBeanDefinitionDocumentReader的registerBeanDefinitions到doRegisterBeanDefinitions,下面我们着重从doRegisterBeanDefinitions的parseBeanDefintions方法开始往下看。

    代理类BeanDefinitionParserDelegate

    负责解析bean的并不是DefaultBeanDefinitionDocumentReader,而是委托给了代理类BeanDefinitionParserDelegate,后面我们会看到BeanDefinitionParserDelegate这个功臣是如何施展拳脚解析bean的。

    parseBeanDefinitions(root, this.delegate);
    

    进入parseBeanDefinitions我们看到其具体实现如下

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
       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);
       }
    }
    
    
    • 判断是否是默认命名空间的,如果是则遍历root下所有的子标签,如果不是,则进行自定义解析
    • 对于是默认命名空间下的标签,在遍历得到每一个子标签后开始调用parseDefaultElement方法开始我们之前提到的默认标签的解析。

    默认标签解析

    前面已经提到过默认标签有四种,我们看看代码也是一目了然,下面是解析默认标签的parseDefaultElement的方法实现

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
       if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
          importBeanDefinitionResource(ele);
       }
       else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
          processAliasRegistration(ele);
       }
       else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
          processBeanDefinition(ele, delegate);
       }
       else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
          // recurse
          doRegisterBeanDefinitions(ele);
       }
    }
    
    
    • if...else语句分别判定了import、alias、bean和beans的情况,并且不同情况作了不同的方法实现
    • 首先要看的就是我们再熟悉不过的bean标签的解析processBeanDefinition方法

    bean标签的解析

    processBeanDefinition的方法实现如下

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
       BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
       if (bdHolder != null) {
          bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
          try {
             // Register the final decorated instance.
             BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
          }
          catch (BeanDefinitionStoreException ex) {
             getReaderContext().error("Failed to register bean definition with name '" +
                   bdHolder.getBeanName() + "'", ele, ex);
          }
          // Send registration event.
          getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
       }
    }
    

    方法并不复杂,大致看下主要干了下面几件事

    • 前面介绍的BeanDefinitionParserDelegate开始排上用场,用于对bean元素解析,并得到BeanDefinitionHolder类型的bdHolder对象
    • 对于解析得到结果不为空的情况,首先还要对于需要修饰的情况(默认标签包含自定义标签)进行处理
    • 然后借助BeanDefinitionReaderUtils的registerBeanDefinition对bdHolder进行注册
    • 最后发送响应事件,通知响应的监听器(Spring3x版本这块还没有具体实现,只是留了个壳)

    delegate.parseBeanDefinitionElement
    我们主要从processBeanDefintion的第一行代码开始,来到BeanDefinitionParserDelegate.parseBeanDefinitionElement

    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
       String id = ele.getAttribute(ID_ATTRIBUTE);
       String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    
       List<String> aliases = new ArrayList<String>();
       if (StringUtils.hasLength(nameAttr)) {
          String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
          aliases.addAll(Arrays.asList(nameArr));
       }
    
       String beanName = id;
       if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
          beanName = aliases.remove(0);
          if (logger.isDebugEnabled()) {
             logger.debug("No XML 'id' specified - using '" + beanName +
                   "' as bean name and " + aliases + " as aliases");
          }
       }
    
       if (containingBean == null) {
          checkNameUniqueness(beanName, aliases, ele);
       }
    
       AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
       if (beanDefinition != null) {
          if (!StringUtils.hasText(beanName)) {
             try {
                if (containingBean != null) {
                   beanName = BeanDefinitionReaderUtils.generateBeanName(
                         beanDefinition, this.readerContext.getRegistry(), true);
                }
                else {
                   beanName = this.readerContext.generateBeanName(beanDefinition);
                   // Register an alias for the plain bean class name, if still possible,
                   // if the generator returned the class name plus a suffix.
                   // This is expected for Spring 1.2/2.0 backwards compatibility.
                   String beanClassName = beanDefinition.getBeanClassName();
                   if (beanClassName != null &&
                         beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                         !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                      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;
             }
          }
          String[] aliasesArray = StringUtils.toStringArray(aliases);
          return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
       }
    
       return null;
    }
    
    
    • 有很多我们之前熟知的细节从这里的代码就可以找到根据,比如“bean如果不声明name属性,spring会默认创建一个name”,显然通过上面一点代码我们就看到Spring确实是这么实现的,而且我们看到如果没有声明name,默认的name值就是id的值
    • 紧接着,我们看下
      AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);这行代码,进入parseBeanDefinitionElement我们会看到这个方法做了很多的解析的工作,比如解析parent属性、解析元数据、解析构造函数、解析property子元素等等

    parseBeanDefinitionElement方法

    public AbstractBeanDefinition parseBeanDefinitionElement(
          Element ele, String beanName, BeanDefinition containingBean) {
    
       this.parseState.push(new BeanEntry(beanName));
    
       String className = null;
       if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
          className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
       }
    
       try {
          String parent = null;
          if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
             parent = ele.getAttribute(PARENT_ATTRIBUTE);
          }
          AbstractBeanDefinition bd = createBeanDefinition(className, parent);
    
          parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
          bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    
          parseMetaElements(ele, bd);
          parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
          parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    
          parseConstructorArgElements(ele, bd);
          parsePropertyElements(ele, bd);
          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;
    }
    
    

    我们挑其中一个解析属性函数parseConstructorArgElements看下具体实现。
    parseConstructorArgElements用于解析ConstructArg标签。
    我们常见的construct-arg大概是这样

    <bean id="user" class="com.jackie.User">
        ​<construct-arg index="0">
        ​    ​<value>jackie</value>
        ​</construct-arg>
        ​<construct-arg index="1">
            <value>male</value>
        </construct-arg>
    </bean>
    

    construct-arg标签的解析过程如下所示

    public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
       String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
       String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
       String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
       if (StringUtils.hasLength(indexAttr)) {
          try {
             int index = Integer.parseInt(indexAttr);
             if (index < 0) {
                error("'index' cannot be lower than 0", ele);
             }
             else {
                try {
                   this.parseState.push(new ConstructorArgumentEntry(index));
                   Object value = parsePropertyValue(ele, bd, null);
                   ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
                   if (StringUtils.hasLength(typeAttr)) {
                      valueHolder.setType(typeAttr);
                   }
                   if (StringUtils.hasLength(nameAttr)) {
                      valueHolder.setName(nameAttr);
                   }
                   valueHolder.setSource(extractSource(ele));
                   if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
                      error("Ambiguous constructor-arg entries for index " + index, ele);
                   }
                   else {
                      bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
                   }
                }
                finally {
                   this.parseState.pop();
                }
             }
          }
          catch (NumberFormatException ex) {
             error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
          }
       }
       else {
          try {
             this.parseState.push(new ConstructorArgumentEntry());
             Object value = parsePropertyValue(ele, bd, null);
             ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
             if (StringUtils.hasLength(typeAttr)) {
                valueHolder.setType(typeAttr);
             }
             if (StringUtils.hasLength(nameAttr)) {
                valueHolder.setName(nameAttr);
             }
             valueHolder.setSource(extractSource(ele));
             bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
          }
          finally {
             this.parseState.pop();
          }
       }
    }
    
    
    • 如果constrcut-arg没有指定index函数,默认按照声明属性的顺序来解析,如果声明则按照index的值解析
    • 使用ConstrcutorArgumentValues的ValueHolder来封装解析得到的type、name等属性值
    • 最后将上面的valueHolder对象按照index添加到BeanDefinition的indexedArgument属性上
    • 注意,对于没有index声明的,最终解析后是存放到genericArgumentValues属性上

    bean标签的注册

    上面层层剥开,一直到解析到具体的属性,我们现在再一层层的往回走。前面所做的工作,无非是将Xml的元素解析出来并与Spring中的BeanDefinition,也就是换了一种形式在Spring中存在。
    我们最终得到的是BeanDefinitionHolder

    public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName, 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;
    }
    

    解析转换工作完成了,现在需要开始bean的注册了

    我们回到DefaultBeanDefinitionDocumentReader的processBeanDefinition,看看注册是如何实现的。
    从DefaultBeanDefinitionDocumentReader.processBeanDefinition->BeanDefinitionReaderUtils.registerBeanDefinition->DefaultLisableBeanFactory.registerBeanFactory。

    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);
          }
       }
    
       synchronized (this.beanDefinitionMap) {
          Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
          if (oldBeanDefinition != null) {
             if (!this.allowBeanDefinitionOverriding) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                      "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                      "': There is already [" + oldBeanDefinition + "] bound.");
             }
             else {
                if (this.logger.isInfoEnabled()) {
                   this.logger.info("Overriding bean definition for bean '" + beanName +
                         "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
             }
          }
          else {
             this.beanDefinitionNames.add(beanName);
             this.frozenBeanDefinitionNames = null;
          }
          this.beanDefinitionMap.put(beanName, beanDefinition);
       }
    
       resetBeanDefinition(beanName);
    }
    
    • 如果beanName已经注册又没有声明可以覆盖,则会抛出异常
    • 符合条件的bean最终会被加入一个map结合
    • map集合的key是beanName,value就是前面解析得到的BeanDefinition实例对象
    • 清楚解析之前留下对应的beanName的缓存

    至此,我们看到了Spring是如何解析标签(我们还有自定义标签没说),如何解析bean标签(我们还有import、alias和beans没说),如何解析标签属性(我们还有太多的属性没有介绍,但是都是类似的过程),最终完成了bean的注册。

    如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

  • 相关阅读:
    html5游戏开发1Aptana 3 eclipse 插件安装
    HTML5 game Development Summary
    前端开发必备的工具
    前端优化方案
    JavaScript 事件冒泡简介及应用(转)
    标准W3C盒子模型和IE盒子模型CSS布局经典盒子模型(转)
    理解.NET中的异常(二)
    异常处理准则
    C#通讯编程
    C#读写文件总结
  • 原文地址:https://www.cnblogs.com/bigdataZJ/p/SpringSource2.html
Copyright © 2020-2023  润新知