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 ...
随机推荐
- Linux 命令 - chmod: 更改文件模式
文件的模式 当使用 ls -l 命令查看目录内容时 [huey@huey-K42JE cmdline]$ ls -l /etc/passwd -rw-r--r-- 1 root root 1917 J ...
- Unity3D 判断鼠标是否按在UGUI上
判断鼠标是否点击在UGUI上 #if UNITY_ANDROID && !UNITY_EDITOR #define ANDROID #endif #if UNITY_IPHONE &a ...
- Remote Desktop Connection Manager介绍
Remote Desktop Connection Manager (RDCMan) 是微软Windows Live体验团队的主要开发者 Julian Burger开发的一个远程桌面管理工具.简称为R ...
- Cocos2d-x实例:设置背景音乐与音效- AppDelegate实现
为了进一步了解背景音乐和音效播放的,我们通过一个实例给大家介绍一下.如下图所示有两个场景:HelloWorld和Setting.在HelloWorld场景点击“游戏设置”菜单可以切换到Setting场 ...
- iOS 拷贝、剪切和粘贴理论基础(转)
简介 在iPhone OS 3.0之后,用户可以在一个应用程序上拷贝文本.图像.或其它数据,然后粘贴到当前或其它应用程序的不同位置上.比如,您可以从某个电子邮件中拷贝一个地址,然后粘贴到Contact ...
- C#中Predicate<T>与Func<T, bool>泛型委托的用法实例
本文以实例形式分析了C#中Predicate<T>与Func<T, bool>泛型委托的用法,分享给大家供大家参考之用.具体如下: 先来看看下面的例子: 1 2 3 4 5 6 ...
- (转)linux下jvm 参数调优
1.基本概念. JAVA_MEM_OPTS=" -server -Xmx2g -Xms2g -Xmn512m -XX:PermSize=128m -Xss256k -XX:+DisableE ...
- (转)C# 数据类型映射 (SQLite,MySQL,MSSQL,Oracle)
一.C# vs SQLite: C# SQLite 字段名 类型 库类型 GetFieldType(#) 转换 备注 F_BOOL bool BIT NOT NULL Boolean F_BOOL_N ...
- 比较X与Y的大小,绝对精准!!!!!!
代码! #include<stdio.h> int max(int a,int b) { int x; x=a+b; return x; } int main() { int i,n,t; ...
- How to: Create Your Own Test Certificate (.pfx)
Original MSDN Link: https://msdn.microsoft.com/en-us/library/ff699202.aspx