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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值