zoukankan      html  css  js  c++  java
  • 3.9 spring-自定义标签解析

      到这里,我们已经完成了分析默认标签的解析与提取过程,或许设计的内容太多,我们忘了我们是冲哪个函数开始了的,

    让我们再次回顾一下默认标签解析方法的起始方法.

    入口如下:

     1 /**
     2      * Parse the elements at the root level in the document: "import", "alias", "bean".
     3      * 
     4      * @param root the DOM root element of the document
     5      */
     6     protected void parseBeanDefinitions(Element root,
     7             BeanDefinitionParserDelegate delegate) {
     8         // 对Bean的处理
     9         if (delegate.isDefaultNamespace(root)) {
    10             NodeList nl = root.getChildNodes();
    11             for (int i = 0; i < nl.getLength(); i++) {
    12                 Node node = nl.item(i);
    13                 if (node instanceof Element) {
    14                     Element ele = (Element) node;
    15                     if (delegate.isDefaultNamespace(ele)) {
    16                         // 对Bean的处理,如果是默认
    17                         parseDefaultElement(ele, delegate);
    18                     }
    19                     else {
    20                         // 对Bean的处理,如果是自定义
    21                         delegate.parseCustomElement(ele);
    22                     }
    23                 }
    24             }
    25         }
    26         else {
    27             delegate.parseCustomElement(root);
    28         }
    29     }

    解析方法如下:

     1     private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
     2         // 对<import>节点, 调用importBeanDefinitionResource方法解析, 此方法中, 又回到第一步读取配置文件并解析. 如此递归循环.
     3         if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
     4             importBeanDefinitionResource(ele);
     5         }
     6         // 对<alias>节点, 调用processAliasRegistration进行别名解析
     7         else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
     8             processAliasRegistration(ele);
     9         }
    10         // <bean>节点调用processBeanDefinition进行解析
    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     }

    核心方法:

     1 /**
     2      * Process the given bean element, parsing the bean definition and registering it with
     3      * the registry.
     4      */
     5     protected void processBeanDefinition(Element ele,
     6             BeanDefinitionParserDelegate delegate) {
     7         // 委托BeanDefinition类的parseBeanDefinitionElement方法进行元素解析,返回Beandefinition
     8         // 类型的实例bdHolder 经过这个方法之后,
     9         // bdHolder实例已经包含了我们配置文件中的各种属性了,例如 : class,name,id,alias
    10         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    11         if (bdHolder != null) {
    12             //当返回的bdHolder 不为空的情况下,若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析.
    13             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
    14             try {
    15                 // Register the final decorated instance.
    16                 // 解析完成之后,需要对解析后的bdHolder 进行注册,同样注册操作委托给了BeanDefinitionUtils 的 registerBeanDefinition
    17                 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,
    18                         getReaderContext().getRegistry());
    19             }
    20             catch (BeanDefinitionStoreException ex) {
    21                 getReaderContext().error(
    22                         "Failed to register bean definition with name '"
    23                                 + bdHolder.getBeanName() + "'", ele, ex);
    24             }
    25             // Send registration event.
    26             // 最后发出响应事件,通知相关的监听器,这个bean已经加载完了.
    27             getReaderContext().fireComponentRegistered(
    28                     new BeanComponentDefinition(bdHolder));
    29         }
    30     }

      之前,我们已经用了大量的时间分析了 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 这句代码;;

    接下来就是我们本章的重点:

      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      首先我们先了解这局话的作用,从语义上讲,这段的码的意识无非是: 如果有需要的话,就对BeanDefinition 进行装饰;那么这句话到底是什么意思呢?
    其实代码适用于这样的一个场景:
    <bean id="animal" class="test.constructor.Animal">
            <myTag:my   value="p1"/> 
        </bean>

      好了,废话不多讲,我们继续分析下这段代码的逻辑;

     1 public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele,
     2             BeanDefinitionHolder definitionHolder) {
     3         /*
     4          * 这里将第三个函数设置为null, 那么第三个函数是做什么用的呢? 什么情况下不为空呢?
     5          * 
     6          * 其实这第三个参数是父类bean,当对某个嵌套配置进行分析时,这里需要传递父类beanDefinition
     7          * .分析源代码得知,这里传递的参数其实是为了使用父类的scope属性,以备子类若没有设置scope 时默认使用父类的属性,这里分析的是顶层配置,所以传null
     8          * ,
     9          */
    10         return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);
    11     }

      这里将第三个函数设置为null, 那么第三个函数是做什么用的呢? 什么情况下不为空呢?

    其实这第三个参数是父类bean,当对某个嵌套配置进行分析时,这里需要传递父类beanDefinition .分析源代码得知,这里传递的参数其实是为了使用父类的scope属性,以备子类若没有设置scope 时默认使用父类的属性,这里分析的是顶层配置,所以传null , 将第三个null 是指为 Null 后进一步追踪;

    代码如下:

     1 public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele,
     2             BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {
     3 
     4         BeanDefinitionHolder finalDefinition = definitionHolder;
     5 
     6         // Decorate based on custom attributes first.
     7         // 遍历所有属性,看看是否有适用于修饰的属性
     8         NamedNodeMap attributes = ele.getAttributes();
     9         for (int i = 0; i < attributes.getLength(); i++) {
    10             Node node = attributes.item(i);
    11             finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
    12         }
    13 
    14         // Decorate based on custom nested elements.
    15         // 遍历所有属性,看看是否有适用于修饰的子元素
    16         NodeList children = ele.getChildNodes();
    17         for (int i = 0; i < children.getLength(); i++) {
    18             Node node = children.item(i);
    19             if (node.getNodeType() == Node.ELEMENT_NODE) {
    20                 finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
    21             }
    22         }
    23         return finalDefinition;
    24     }

      上面的代码,我们可以看到该方法分别对元素的所有属性和节点进行了decorateIfRequired()方法的调用,我们继续跟踪代码

     1     public BeanDefinitionHolder decorateIfRequired(Node node,
     2             BeanDefinitionHolder originalDef, BeanDefinition containingBd) {
     3         // 获取命名空间
     4         String namespaceUri = getNamespaceURI(node);
     5         // 对非默认标签进行修饰
     6         if (!isDefaultNamespace(namespaceUri)) {
     7             // 根据命名空间找到对应的处理器
     8             NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(
     9                     namespaceUri);
    10             if (handler != null) {
    11                 // 进行修饰
    12                 return handler.decorate(node, originalDef, new ParserContext(
    13                         this.readerContext, this, containingBd));
    14             }
    15             else if (namespaceUri != null
    16                     && namespaceUri.startsWith("http://www.springframework.org/")) {
    17                 error("Unable to locate Spring NamespaceHandler for XML schema namespace ["
    18                         + namespaceUri + "]", node);
    19             }
    20             else {
    21                 // A custom namespace, not to be handled by Spring - maybe "xml:...".
    22                 if (logger.isDebugEnabled()) {
    23                     logger.debug("No Spring NamespaceHandler found for XML schema namespace ["
    24                             + namespaceUri + "]");
    25                 }
    26             }
    27         }
    28         return originalDef;
    29     }

      

      程序走到这里已经相当清楚了,首先获取属性或者元素的命名空间,以此来判断该元素或属性是否适用于自定义标签的解析条件,找出自定义类型对应的

    NamespaceHandler 并进一步解析,
      我们总结一下 decorateIfRequired 的作用,在 decorateIfRequired 中,我们可以看到对于程序默认的属性或元素是直接忽略过去的,因为在之前已经处理过了,
    这里只对自定义标签感兴趣,在这个方法中实现了寻找自定义标签,并根据自定义标签寻找命名空间处理器,并进一步的解析;


  • 相关阅读:
    Js变量类型
    前端面试题HTML
    AP聚类
    锚点
    html
    Active Learning主动学习
    z-index
    position
    学习笔记_卷积神经网络
    学习笔记_深度学习的数学基础
  • 原文地址:https://www.cnblogs.com/mjorcen/p/3648799.html
Copyright © 2011-2022 走看看