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) —— 自定义标签的更多相关文章

  1. spring源码学习之默认标签的解析(一)

    继续spring源码的学习之路,现在越来越觉得这个真的很枯燥的,而且我觉得要是自己来看源码,真的看不下去,不是没有耐心,而是真的没有头绪,我觉得结合着书来看,还是很有必要的,最起码大致的流程是能够捋清 ...

  2. spring源码学习之默认标签的解析(二)

    这个是接着上一篇来写,主要是这章内容比较多,还是分开来写吧! 一.AbstractBeanDefinition属性介绍 XML中的所有的属性都可以在GenericBeanDefinition中找到对应 ...

  3. spring源码研究2 自定义标签实现及使用

    1.自定义标签实现及使用参考: http://blog.csdn.net/fighterandknight/article/details/50112701 1)创建一个需要扩展的组件 User.ja ...

  4. Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md

    写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...

  5. Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签

    写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...

  6. Spring源码学习

    Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ...

  7. Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作

    写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...

  8. Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点

    Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...

  9. spring源码学习之路---IOC初探(二)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...

随机推荐

  1. Windows10系统tensorflow-gpu安装

    准备工作 安装前请确保自己的显卡支持gpu加速,支持加速的gpu型号可在下面的链接中查询. https://www.geforce.com/hardware/technology/cuda/suppo ...

  2. LeetCode 96——不同的二叉搜索树

    1. 题目 2. 解答 以 \(1, 2, \cdots, n\) 构建二叉搜索树,其中,任意数字都可以作为根节点来构建二叉搜索树.当我们将某一个数字作为根节点后,其左边数据将构建为左子树,右边数据将 ...

  3. centos+nginx+redmine+gitosis安装指南

    说明 这篇文章我现在的主要目的是记录自己安装redmine和gitosis的过程,可能写的有些糙,请各位读者见谅.我会在后面的时间里逐渐完善细节.但我想,这已经是网上迄今为止国内最详细的nginx+r ...

  4. 1.Hadoop介绍

    1. Hadoop介绍 1.1 什么是Hadoop 开源的,可靠的,分布式的,可伸缩的 提供的功能: 利用服务器集群,根据用户的自定义业务逻辑,对海量数据进行分布式处理 1.2 处理方式 大众角度 数 ...

  5. Android中的回调Callback

    回调就是外部设置一个方法给一个对象, 这个对象可以执行外部设置的方法, 通常这个方法是定义在接口中的抽象方法, 外部设置的时候直接设置这个接口对象即可. 例如给安卓添加按钮点击事件, 我们创建了OnC ...

  6. 福大软工1816:Alpha(5/10)

    Alpha 冲刺 (5/10) 队名:第三视角 组长博客链接 本次作业链接 团队部分 团队燃尽图 工作情况汇报 张扬(组长) 过去两天完成了哪些任务: 文字/口头描述: 1.忙于复习,本次无成果 展示 ...

  7. lintcode-163-不同的二叉查找树

    163-不同的二叉查找树 给出 n,问由 1...n 为节点组成的不同的二叉查找树有多少种? 样例 给出n = 3,有5种不同形态的二叉查找树: 标签 卡特兰数 动态规划 思路 参考博客http:// ...

  8. TCP系列37—Keep Alive—1、TCP存活检测

    一.TCP存活(keepalive)检测的背景 对于TCP设计来说,如果一个客户端和服务器端建立连接后,不在进行数据传输,那么这个连接将会一直存在下去,理论上即使中间的路由器崩溃重启.或者中间的网络线 ...

  9. <Android>tab选项卡

    1.继承TabActivity实现 a)         在布局文件中使用FrameLayout列出Tab组件及Tab中的内容组件 b)        Activity要继承TabActivity c ...

  10. 3dContactPointAnnotationTool开发日志(二三)

      smpl模型得是一个整体,于是我让子物体的选项卡的删除按钮消失,这样就不会删除不必要的东西然后产生奇怪现象: