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

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

入口如下:

 /**
* Parse the elements at the root level in the document: "import", "alias", "bean".
*
* @param root the DOM root element of the document
*/
protected void parseBeanDefinitions(Element root,
BeanDefinitionParserDelegate delegate) {
// 对Bean的处理
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)) {
// 对Bean的处理,如果是默认
parseDefaultElement(ele, delegate);
}
else {
// 对Bean的处理,如果是自定义
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

解析方法如下:

     private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 对<import>节点, 调用importBeanDefinitionResource方法解析, 此方法中, 又回到第一步读取配置文件并解析. 如此递归循环.
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 对<alias>节点, 调用processAliasRegistration进行别名解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// <bean>节点调用processBeanDefinition进行解析
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// <beans>的处理
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}

核心方法:

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

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

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

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

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

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

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

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

代码如下:

 public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele,
BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) { BeanDefinitionHolder finalDefinition = definitionHolder; // Decorate based on custom attributes first.
// 遍历所有属性,看看是否有适用于修饰的属性
NamedNodeMap attributes = ele.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
} // Decorate based on custom nested elements.
// 遍历所有属性,看看是否有适用于修饰的子元素
NodeList children = ele.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}

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

     public BeanDefinitionHolder decorateIfRequired(Node node,
BeanDefinitionHolder originalDef, BeanDefinition containingBd) {
// 获取命名空间
String namespaceUri = getNamespaceURI(node);
// 对非默认标签进行修饰
if (!isDefaultNamespace(namespaceUri)) {
// 根据命名空间找到对应的处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(
namespaceUri);
if (handler != null) {
// 进行修饰
return handler.decorate(node, originalDef, new ParserContext(
this.readerContext, this, containingBd));
}
else if (namespaceUri != null
&& namespaceUri.startsWith("http://www.springframework.org/")) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace ["
+ namespaceUri + "]", node);
}
else {
// A custom namespace, not to be handled by Spring - maybe "xml:...".
if (logger.isDebugEnabled()) {
logger.debug("No Spring NamespaceHandler found for XML schema namespace ["
+ namespaceUri + "]");
}
}
}
return originalDef;
}

  

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

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

3.9 spring-自定义标签解析的更多相关文章

  1. Spring自定义标签解析与实现

           在Spring Bean注册解析(一)和Spring Bean注册解析(二)中我们讲到,Spring在解析xml文件中的标签的时候会区分当前的标签是四种基本标签(import.alias ...

  2. Spring 自定义标签配置

    前景:经常使用一些依赖于Spring的组件时,发现可以通过自定义配置Spring的标签来实现插件的注入,例如数据库源的配置,Mybatis的配置等.那么这些Spring标签是如何自定义配置的?学习Sp ...

  3. spring源码深度解析— IOC 之 自定义标签解析

    概述 之前我们已经介绍了spring中默认标签的解析,解析来我们将分析自定义标签的解析,我们先回顾下自定义标签解析所使用的方法,如下图所示: 我们看到自定义标签的解析是通过BeanDefinition ...

  4. 这一次搞懂Spring自定义标签以及注解解析原理

    前言 在上一篇文章中分析了Spring是如何解析默认标签的,并封装为BeanDefinition注册到缓存中,这一篇就来看看对于像context这种自定义标签是如何解析的.同时我们常用的注解如:@Se ...

  5. 自己构建一个Spring自定义标签以及原理讲解

    平时不论是在Spring配置文件中引入其他中间件(比如dubbo),还是使用切面时,都会用到自定义标签.那么配置文件中的自定义标签是如何发挥作用的,或者说程序是如何通过你添加的自定义标签实现相应的功能 ...

  6. spring基础---->spring自定义标签(一)

    Spring具有一个基于架构的扩展机制,可以使用xml文件定义和配置bean.本博客将介绍如何编写自定义XML bean的解析器,并用实例来加以说明.其实我一直相信 等你出现的时候我就知道是你. Sp ...

  7. spring自定义标签之 自我实现

     引言: 最近心情比较难以平静,周末的两天就跑出去散心了,西湖边上走走,看日落,还是不错的.回来博客上发现,在自定义标签上,最后一步实现忘记加上了.其实,人生的路程中,我们总是实现着自我的价值,让自己 ...

  8. Spring自定义标签的实现

    首先 简单写下 spring xml解析的过程 通过一段简单的 调用spring代码开始 public static void main(String[] args) { ApplicationCon ...

  9. angularjs directive (自定义标签解析)

    angularjs directive (自定义标签解析) 定义tpl <!-- 注意要有根标签 --> <div class="list list-inset" ...

  10. spring自定义标签之 规范定义XSD

    引言: spring的配置文件中,一切的标签都是spring定义好的.<bean/>等等,有了定义的规范,才能让用户填写的正常可用.想写自定义标签,但首先需要了解XML Schema De ...

随机推荐

  1. 取消Win7任务栏窗口自动排序

    点击“开始”菜单,在“搜索程序和文件”框中输入“关闭自动窗口排列”,找到后打开,找到“防止将窗口移动到屏幕边缘时自动排列窗口”这一项,勾上后点击确定就可以了.

  2. Java Concurrency - Lock

    Lock 是 Java API 提供的另一种线程同步机制,它提供了比 synchronized 关键字更为灵活.强大的锁定操作. 锁是控制多个线程对共享资源进行访问的工具.通常,所提供了对共享资源的独 ...

  3. Spring(3.2.3) - Beans(10): 生命周期

    Spring 容器可以管理 singleton 作用域 Bean 的生命周期,容器能够跟踪 Bean 实例的创建.销毁.管理 Bean 生命周期行为主要有两个时机: 注入 Bean 的依赖关系之后 即 ...

  4. HTTP - 首部

    首部类型 首部类型 说明  通用首部   客户端和服务器都可以使用的通用首部.可以在客户端.服务器和其他应用程序之间提供一些有用的通用首部.  请求首部   请求首部时请求报文特有的.它们为服务器提供 ...

  5. asp中utf8不会出现乱码的写法

    <%@ CODEPAGE=65001 %> <% Response.CodePage=65001%> <% Response.Charset="UTF-8&qu ...

  6. oracle中比较两表表结构差异和数据差异的方法

    在工作中需要完成这么一个需求:比较两个表的表结构是否形相同,并找出差异.比较两个表中的数据是否相同,并找出差异数据?    分析:由于表结构中字段比较多,手工比较很浪费时间,而且不能保证不出错误.对于 ...

  7. 32位系统下使用4GB内存

    64位系统的驱动还有不少缺陷,果断重装回32位系统,但是4gb的内存,明显是浪费啊. 所以必须利用起来. 我没有采用不稳定的破解内核的做法,采用了虚拟硬盘的做法.因为个人觉得这样其实利用效率更高. 方 ...

  8. (转)SQLSERVER表分区的介绍(一)

    下面进入正题吧,很多时候当单张数据表的数据量比较大的时候比如千万级别条记录.上亿级别记录,如果不做优化,那么查询的效率大家清楚. 有经验的人会通过各种手段做优化,其中表分区就是其中一种手段. 个人对表 ...

  9. (转)jquery ajax使用及跨域访问解决办法

    原文地址:***/UIweb/jquery_ajax_kuayujiejue.html 最近开发中,设计到智能手机项目,给领导做几个demo.主要是用jquery和jqeury mobile. 越来越 ...

  10. SQL 存储过程加事务的使用

    create proc USP_CUTTING_TATABLET_PULL_FINISH ( @name NVARCHAR(20) ) as SET XACT_ABORT ON--设置全盘回滚 BEG ...