3.9 spring-自定义标签解析
到这里,我们已经完成了分析默认标签的解析与提取过程,或许设计的内容太多,我们忘了我们是冲哪个函数开始了的,
让我们再次回顾一下默认标签解析方法的起始方法.
入口如下:
/**
* 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-自定义标签解析的更多相关文章
- Spring自定义标签解析与实现
在Spring Bean注册解析(一)和Spring Bean注册解析(二)中我们讲到,Spring在解析xml文件中的标签的时候会区分当前的标签是四种基本标签(import.alias ...
- Spring 自定义标签配置
前景:经常使用一些依赖于Spring的组件时,发现可以通过自定义配置Spring的标签来实现插件的注入,例如数据库源的配置,Mybatis的配置等.那么这些Spring标签是如何自定义配置的?学习Sp ...
- spring源码深度解析— IOC 之 自定义标签解析
概述 之前我们已经介绍了spring中默认标签的解析,解析来我们将分析自定义标签的解析,我们先回顾下自定义标签解析所使用的方法,如下图所示: 我们看到自定义标签的解析是通过BeanDefinition ...
- 这一次搞懂Spring自定义标签以及注解解析原理
前言 在上一篇文章中分析了Spring是如何解析默认标签的,并封装为BeanDefinition注册到缓存中,这一篇就来看看对于像context这种自定义标签是如何解析的.同时我们常用的注解如:@Se ...
- 自己构建一个Spring自定义标签以及原理讲解
平时不论是在Spring配置文件中引入其他中间件(比如dubbo),还是使用切面时,都会用到自定义标签.那么配置文件中的自定义标签是如何发挥作用的,或者说程序是如何通过你添加的自定义标签实现相应的功能 ...
- spring基础---->spring自定义标签(一)
Spring具有一个基于架构的扩展机制,可以使用xml文件定义和配置bean.本博客将介绍如何编写自定义XML bean的解析器,并用实例来加以说明.其实我一直相信 等你出现的时候我就知道是你. Sp ...
- spring自定义标签之 自我实现
引言: 最近心情比较难以平静,周末的两天就跑出去散心了,西湖边上走走,看日落,还是不错的.回来博客上发现,在自定义标签上,最后一步实现忘记加上了.其实,人生的路程中,我们总是实现着自我的价值,让自己 ...
- Spring自定义标签的实现
首先 简单写下 spring xml解析的过程 通过一段简单的 调用spring代码开始 public static void main(String[] args) { ApplicationCon ...
- angularjs directive (自定义标签解析)
angularjs directive (自定义标签解析) 定义tpl <!-- 注意要有根标签 --> <div class="list list-inset" ...
- spring自定义标签之 规范定义XSD
引言: spring的配置文件中,一切的标签都是spring定义好的.<bean/>等等,有了定义的规范,才能让用户填写的正常可用.想写自定义标签,但首先需要了解XML Schema De ...
随机推荐
- 自定义表单样式之checkbox和radio
1,起因 最近在工作中要实现自定义式的radio样式,而我们通常使用的时默认的样式,因为自己实在想不到解决的方法,于是开始搜索,最终看到了不错的解决办法,可以完美解决我们遇到的问题. 2,原理 大家都 ...
- Linux 命令 - alias: 设置或显示别名
当一个命令太长或者不符合用户的习惯,那么可以为该命令指定一个符合用户习惯的别名.比如习惯 DOS 命令的用户可以使用 alias md='mkdir' 命令将 md 来替换 mkdir.有时也为经常调 ...
- 获取登录的IP或者信息
这是转载的,也不想去检查性能,对于这些成熟的代码,发在这里完全是懒,仅此而已! 1.获取客户端IP /// <summary> /// 获取客户端Ip /// </summary&g ...
- Android Studio ndk-Jni开发详细
http://www.open-open.com/lib/view/open1451917048573.html Java Native Interface (JNI)标准是java平台的一部分,它允 ...
- 在win7下配置java编译环境变量
今天刚接触java编程,环境的配置方法比较复杂.好记性不如烂笔头,发个文章记录一下吧. win7系统 Jdk版本1.6 用鼠标右击“我的电脑”->属性->高级->环境变量系统变量-& ...
- Jquery 模糊匹配ID
[属性名称] 匹配包含给定属性的元素[att=value] 匹配包含给定属性的元素 (大小写区分)[att*=value] 模糊匹配[att!=value] 不能是这个值[att$=value] 结尾 ...
- Unity3D项目实战笔记(5):延时功能的几种实现
我所做过的系统,分单机版系统(2005年).CS系统(2010年).实时系统(2015年),各个系统均有“延时”功能:定时调度的: 本博客说的是实时系统中的延时功能(基于Unity3D游戏引擎). 在 ...
- axure注册码
ahjesus Axure RP 7.0注册码 用户名:axureuser 序列号:8wFfIX7a8hHq6yAy6T8zCz5R0NBKeVxo9IKu+kgKh79FL6IyPD6lK7G6+t ...
- Js 变量的作用域
变量的作用域 所有的变量作用域是指在哪里可用哪里不可用 局部作用域和全局作用域 在js中以函数来分割作用域 在函数里面就叫做局部作用域,在函数外面的就叫全局作用域 在函数里面如果省略var 关键字 那 ...
- 文档生产工具 Doxygen
Doxygen是一种开源跨平台的,类似JavaDoc风格描述的文档系统,支持C.C++.Java.Objective-C等语言.可以从一套归档源文件开始,生成HTML,XML,pdf等不同风格的格式. ...