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(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...
随机推荐
- 通过Ajax上传文件至WebApi服务器
https://stackoverflow.com/questions/43013858/ajax-post-a-file-from-a-form-with-axios var formData = ...
- mouseover 和 mouseout 事件是可以冒泡的 取消
mouseover 和 mouseout 事件是可以冒泡的,子元素上触发的事件会冒泡到父元素上.可以改用 mouseleave 和 mouseenter 事件,这两个事件不冒泡.
- hive创建外部表
Create [EXTERNAL] TABLE [IF NOT EXISTS] table_name [(col_name data_type [COMMENT col_comment], ...)] ...
- 编译安装hadoop2.6.3
一.安装环境 1.1 JAVA 安装java1.7 下载jdk1.7: [root@node1~]# wget http://download.oracle.com/otn-pub/java/jd ...
- [leetcode-667-Beautiful Arrangement II]
Given two integers n and k, you need to construct a list which contains n different positive integer ...
- scrapy(2)——scrapy爬取新浪微博(单机版)
Sina爬虫教程 Scrapy环境搭建 环境:window10 + python2.7(包含scrapy)+ mongoDB 1.1 安装集成了python2.7的anaconda ana ...
- iOS- 优化与封装 APP音效的播放
1.关于音效 音效又称短音频,是一个声音文件,在应用程序中起到点缀效果,用于提升应用程序的整体用户体验. 我们手机里常见的APP几乎都少不了音效的点缀. 显示实现音效并不复杂,但对我们App很 ...
- 实验吧密码学:RSAROLL
原题: {920139713,19} 704796792 752211152 274704164 18414022 368270835 483295235 263072905 459788476 48 ...
- delphi7中 OnDrawColumnCell 事件怎么用
你问的这个事件应该是dbgrid控件中的吧?这个事件是在grid控件载入数据的时候触发的,至于你这个“怎么用”波及的范围太大了,呵呵!不知道如何说起!另外还是发一段相关的代码吧,这也是我之前提过问题, ...
- js 复制到剪贴板 兼容还得自己想办法
clipboard.js https://github.com/zenorocha/clipboard.js/ 主要问题还是ie8, 可以使用ie 特有的方法 if (window.clipboard ...