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 ...
随机推荐
- 核心概念 —— 门面(Facades)
1.简介 门面为应用的服务容器中的绑定类提供了一个"静态"接口.Laravel 内置了很多门面,你可能在不知道的情况下正在使用它们.Laravel 的门面作为服务容器中的底层类的& ...
- MyBatis(3.2.3) - Configuring MyBatis using XML, typeAliases
In the SQL Mapper configuration file, we need to give the fully qualified name of the JavaBeans for ...
- CSS之边框属性
border-style 属性用于设置元素所有边框的样式,或者单独地为各边设置边框样式. border-style:dotted solid double dashed; border-style的属 ...
- jquery之音乐均衡器
制作这个音乐均衡器需要一个equalizer插件(插件我已经上传),下面介绍一下网页的BGM的相关属性: hidden="true"表示隐藏播放,即不显示播放器的外观,若要想显示, ...
- 大数据技术 —— MapReduce 简介
本文为senlie原创,转载请保留此地址:http://www.cnblogs.com/senlie/ 1.概要很多计算在概念上很直观,但由于输入数据很大,为了能在合理的时间内完成,这些计算必须分布在 ...
- HTML5的Web SQL Database
本文将介绍 Web SQL Database 规范中定义的三个核心方法: openDatabase:这个方法使用现有数据库或新建数据库来创建数据库对象 transaction:这个方法允许我们根据情况 ...
- OC10_数组的内存管理
// // main.m // OC10_数组的内存管理 // // Created by zhangxueming on 15/6/18. // Copyright (c) 2015年 zhangx ...
- 【转载】茶叶蛋干货!《超容易的Linux系统管理入门书》(连载十)进行动态主机配置DHCP
使用动态主机配置协议DHCP(Dynamic Host Configuration Protocol)则可以避免网络参数变化后一些繁琐的配置,客户端可以从DHCP服务端检索相关信息并完成相关网络配置, ...
- 使用httpclient发送post请求与get请求
最近因为项目的要求,需要使用httpclient来发送请求.但是查阅了许多博客,大家发送请求的方法各不相同.原因是因为httpclient的jar包的不同版本,其内部方法也不相同.因此抛开具体用到的j ...
- VS2013编译WEBKIT
0,安装VS2013:DXSDK_Jun10.exe:QuickTimeSDK.exe 1,WebKit-r174650.tar.bz2 以管理员解压(非管理员解压最后几下总是报错) 2,设置环境变量 ...