Spring 源码学习(1) —— 自定义标签
Spring 工作流程是先加载解析xml配置文件:配置文件中存在默认的标签,也可以自定义标签。解析默认标签调用:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
解析自定义标签调用:
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
parseCustomElement
下面我们正式开始搞Spring自定义标签:
1.搞一个Bean: 这是一个很普通的pojo, 只是用来接收配置文件。
/**
* @filename: User.java
* @author: Wang Chinda
* @date: 2018-05-21
* @version: v1.0
* @modify_history: -
* 20180521 Wang Chinda create
* 20180521 Wang Chinda modify method()
*/
package com.itdoc.learn.source.custom; /**
* @desc 自定义标签测试类
* @author Wang Chinda
* @create 2018-05-21 14:03
*/
public class User {
private String id;
private String userName;
private String email; public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} public String getEmail() {
return email;
} public void setEmail(String email) {
this.email = email;
}
}
User
2.定义一个文件描述组件[xml schama difinition (xsd)]: 在classpath下创建一个META-INF文件夹, 在此文件夹下创建一个spring-user.xsd文件。
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.itdoc.com/schema/user"
targetNamespace="http://www.itdoc.com/schema/user"
elementFormDefault="qualified">
<xsd:element name="user">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="userName" type="xsd:string" />
<xsd:attribute name="email" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:schema>
spring-user
targetNamespace="http://www.itdoc.com/schema/user" 和xmlns="http://www.itdoc.com/schema/user"都是自定义的。
文件中的片段意义:
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
显示 schema 中用到的元素和数据类型来自命名空间 "http://www.w3.org/2001/XMLSchema"。同时它还规定了来自命名空间 "http://www.w3.org/2001/XMLSchema" 的元素和数据类型应该使用前缀 xsd
xmlns="http://www.itdoc.com/schema/user"
指出默认的命名空间是 "http://www.itdoc.com/schema/user"
targetNamespace="http://www.itdoc.com/schema/user"
显示被此 schema 定义的元素 (note, to, from, heading, body) 来自命名空间: "http://www.itdoc.com/schema/user"
elementFormDefault="qualified"
指出任何 XML 实例文档所使用的且在此 schema 中声明过的元素必须被命名空间限定
在上面的xsd文件描述中添加一个targetNamespace, 并在这个命名空间中定义了一个name为user的element, user有3个属性id、userName、email, 三个属性均为string类型。
3.创建一个用来解析xsd文件的定义和组件定义的文件并实现BeanDefinitionParser接口。此例中我们继承AbstractSingleBeanDefinitionParser抽象类.

/**
* @filename: UserBeanDefinitionParse.java
* @author: Wang Chinda
* @date: 2018-05-21
* @version: v1.0
* @modify_history: -
* 20180521 Wang Chinda create
* 20180521 Wang Chinda modify method()
*/
package com.itdoc.learn.source.custom; import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element; /**
* @desc 自定义bean定义解析
* @author Wang Chinda
* @create 2018-05-21 14:20
*/
public class UserBeanDefinitionParse extends AbstractSingleBeanDefinitionParser { /**
* Element对应的类
* @param element
* @return
*/
@Override
protected Class<?> getBeanClass(Element element) {
return User.class;
} @Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String userName = element.getAttribute("userName");
String email = element.getAttribute("email"); // 将提取到的数据放入到BeanDefinitionBuilder中, 待完成所有bean的解析后统一注册到beanFactory中
if (StringUtils.hasText(userName)) {
builder.addPropertyValue("userName", userName);
} if (StringUtils.hasText(email)) {
builder.addPropertyValue("email", email);
}
}
}
UserBeanDefinitionParse
4.创建一个Handler文件, 扩展自NamespaceHandlerSupport, 目的是将组件注册到Spring容器中
/**
* @filename: MyNameSpaceHandler.java
* @author: Wang Chinda
* @date: 2018-05-21
* @version: v1.0
* @modify_history: -
* 20180521 Wang Chinda create
* 20180521 Wang Chinda modify method()
*/
package com.itdoc.learn.source.custom; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; /**
* @desc 自定义命名空间控制器
* @author Wang Chinda
* @create 2018-05-21 14:28
*/
public class MyNameSpaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParse());
}
}
MyNameSpaceHandler
以上代码功能是当遇到<xxx:user 这样开头的元素, 就会交给UserBeanDefinitionParse去解析。
5.编写spring.handlers和spring.schemas文件, 默认位置是在classpath:/META-INF/文件夹下
注意:
1) 这两个文件名不允许改,若想更改的话,得需要连带更改spring源码文件。
2) 为什么将spring.handlers和spring.schemas放在META-INF文件夹下, 因为Spring默认到这个文件夹中寻找spring.handlers和spring.schemas文件。
spring.handlers:
http\://www.itdoc.com/schema/user=com.itdoc.learn.source.custom.MyNameSpaceHandler
spring.schemas:
http\://www.itdoc.com/schema/user.xsd=META-INF/spring-user.xsd
注意:
1) 冒号用来转译的。
2) 命名空间一定不要写错, 要和targetNamespace中定义的相同。
Spring加载自定义标签的大致流程是遇到自定义标签就会去spring.handler和spring.schemas中去找对应的handler和xsd,进而找到对应的MyNameSpaceHandler以及解析元素的UserBeanDefinitionParse, 从而完成了整个自定义标签的解析。
6.创建测试配置文件, 在文件中引入对应的命名空间及xsd, 便可以使用自定义标签。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:myTag="http://www.itdoc.com/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.itdoc.com/schema/user http://www.itdoc.com/schema/user.xsd"> <myTag:user id="customBeanTest" userName="name" email="itdoc@163.com"/> </beans>
7.测试
/**
* @filename: CustomMain.java
* @author: Wang Chinda
* @date: 2018-05-21
* @version: v1.0
* @modify_history: -
* 20180521 Wang Chinda create
* 20180521 Wang Chinda modify method()
*/
package com.itdoc.learn.source.custom; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; /**
* @desc 测试
* @author Wang Chinda
* @create 2018-05-21 14:58
*/
public class CustomMain { public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test/customTest.xml");
User user = (User) applicationContext.getBean("customBeanTest");
System.out.println(user.getEmail() + " ---- " + user.getUserName());
}
}
控制台显示:

GitHub地址: https://github.com/wcd19901010/spring-01
Spring 源码学习(1) —— 自定义标签的更多相关文章
- spring源码学习之默认标签的解析(一)
继续spring源码的学习之路,现在越来越觉得这个真的很枯燥的,而且我觉得要是自己来看源码,真的看不下去,不是没有耐心,而是真的没有头绪,我觉得结合着书来看,还是很有必要的,最起码大致的流程是能够捋清 ...
- spring源码学习之默认标签的解析(二)
这个是接着上一篇来写,主要是这章内容比较多,还是分开来写吧! 一.AbstractBeanDefinition属性介绍 XML中的所有的属性都可以在GenericBeanDefinition中找到对应 ...
- spring源码研究2 自定义标签实现及使用
1.自定义标签实现及使用参考: http://blog.csdn.net/fighterandknight/article/details/50112701 1)创建一个需要扩展的组件 User.ja ...
- Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md
写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...
- Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签
写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...
- Spring源码学习
Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ...
- Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作
写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...
- Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点
Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...
- spring源码学习之路---IOC初探(二)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...
随机推荐
- CryptoZombies学习笔记——Lesson5
chapter1:token代币 简而言之,通证就是支持交易的包含一系列规范的函数接口的一个智能合约,发币可以用ERC20标准,但是像僵尸这种非同质化代币,需要用ERC721标准 chapter2:e ...
- 特殊符号 & 以太坊
&表示取二进制的末尾 &1表示如果末尾是奇数和偶数两种情况 0 偶数 1奇数 举例子: int a=1;int p=&a; 其中,p是指针,&a就是将a在内存中的实际地 ...
- lvs+keepalived详解
常用软件安装及使用目录 资源链接:https://pan.baidu.com/s/15rFjO-EnTOyiTM7YRkbxuA 网盘分享的文件在此 官网:http://www.linuxvir ...
- 1.Hadoop介绍
1. Hadoop介绍 1.1 什么是Hadoop 开源的,可靠的,分布式的,可伸缩的 提供的功能: 利用服务器集群,根据用户的自定义业务逻辑,对海量数据进行分布式处理 1.2 处理方式 大众角度 数 ...
- 第一章 Windows编程基础(1~4课)
第一课:从main到WinMain 第二课:窗口和消息 第三课:MFC编程 第四课:MFC应用程序框架 概括: Win32的两种编程框架:SDK方式.MFC方式 1. SDK方式:使用WinMain入 ...
- Java学习个人备忘录之文档注释
文档注释 单行注释用 // 多行注释有两种,第一种是 /* 内容 */,第二种是/** 内容 */. 这两种多行注释的区别是/** 内容 */这种注释可以生成一个该文件的注释文档,下面是演示代码. A ...
- javaIO--字符流
java提供字符流对自否刘式文件进行数据读写操作.字符输入流类是Reader及其子类,输出流是Writer及其子类. 另外,上一篇javaIO写的是字节流,字节流方式也可以对以字符为基本类型的流式文件 ...
- SQL 中 Date 与Datetime的区别
Date是SQL Server 2008新引进的数据类型.它表示一个日子,不包含时间部分,可以表示的日期范围从公元元年1月1日到9999年12月31日.只需要3个字节的存储空间. DateTime 日 ...
- bzoj4332[JSOI2012]分零食
一下午被这题的精度续掉了...首先可以找出一个多项式的等比数列的形式,然后类似poj的Matrix Series,不断倍增就可以了.用复数点值表示进行多次的多项式运算会刷刷地炸精度...应当用int存 ...
- Python 源码剖析(三)【字符串对象】
三.字符串对象 1.PyStringObject与PyString_Type 2.创建PyStringObject对象 3.Intern 机制 4.字符缓冲池 5.PyStringObject 效率相 ...