Spring 源码学习(1) —— 自定义标签

Spring 工作流程是先加载解析xml配置文件:配置文件中存在默认的标签,也可以自定义标签。解析默认标签调用:

 1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
 2         if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
 3             importBeanDefinitionResource(ele);
 4         }
 5         else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
 6             processAliasRegistration(ele);
 7         }
 8         else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
 9             processBeanDefinition(ele, delegate);
10         }
11         else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
12             // recurse
13             doRegisterBeanDefinitions(ele);
14         }
15     }
View Code

解析自定义标签调用:

1 public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
2         String namespaceUri = getNamespaceURI(ele);
3         NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
4         if (handler == null) {
5             error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
6             return null;
7         }
8         return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
9     }
parseCustomElement

下面我们正式开始搞Spring自定义标签:


1.搞一个Bean: 这是一个很普通的pojo, 只是用来接收配置文件。

 1 /**
 2  * @filename: User.java
 3  * @author: Wang Chinda
 4  * @date: 2018-05-21
 5  * @version: v1.0
 6  * @modify_history: -
 7  * 20180521   Wang Chinda   create
 8  * 20180521   Wang Chinda   modify   method()
 9  */
10 package com.itdoc.learn.source.custom;
11 
12 /**
13  * @desc 自定义标签测试类
14  * @author Wang Chinda
15  * @create 2018-05-21 14:03
16  */
17 public class User {
18     private String id;
19     private String userName;
20     private String email;
21 
22     public String getId() {
23         return id;
24     }
25 
26     public void setId(String id) {
27         this.id = id;
28     }
29 
30     public String getUserName() {
31         return userName;
32     }
33 
34     public void setUserName(String userName) {
35         this.userName = userName;
36     }
37 
38     public String getEmail() {
39         return email;
40     }
41 
42     public void setEmail(String email) {
43         this.email = email;
44     }
45 }
User

 

 2.定义一个文件描述组件[xml schama difinition (xsd)]: 在classpath下创建一个META-INF文件夹, 在此文件夹下创建一个spring-user.xsd文件。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 3             xmlns="http://www.itdoc.com/schema/user"
 4             targetNamespace="http://www.itdoc.com/schema/user"
 5             elementFormDefault="qualified">
 6     <xsd:element name="user">
 7         <xsd:complexType>
 8             <xsd:attribute name="id" type="xsd:string" />
 9             <xsd:attribute name="userName" type="xsd:string" />
10             <xsd:attribute name="email" type="xsd:string" />
11         </xsd:complexType>
12     </xsd:element>
13 </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抽象类.

 

 1 /**
 2  * @filename: UserBeanDefinitionParse.java
 3  * @author: Wang Chinda
 4  * @date: 2018-05-21
 5  * @version: v1.0
 7  * @modify_history: -
 8  * 20180521   Wang Chinda   create
 9  * 20180521   Wang Chinda   modify   method()
10  */
11 package com.itdoc.learn.source.custom;
12 
13 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
14 import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
15 import org.springframework.util.StringUtils;
16 import org.w3c.dom.Element;
17 
18 /**
19  * @desc 自定义bean定义解析
20  * @author Wang Chinda
21  * @create 2018-05-21 14:20
22  */
23 public class UserBeanDefinitionParse extends AbstractSingleBeanDefinitionParser {
24 
25     /**
26      * Element对应的类
27      * @param element
28      * @return
29      */
30     @Override
31     protected Class<?> getBeanClass(Element element) {
32         return User.class;
33     }
34 
35     @Override
36     protected void doParse(Element element, BeanDefinitionBuilder builder) {
37         String userName = element.getAttribute("userName");
38         String email = element.getAttribute("email");
39 
40         // 将提取到的数据放入到BeanDefinitionBuilder中, 待完成所有bean的解析后统一注册到beanFactory中
41         if (StringUtils.hasText(userName)) {
42             builder.addPropertyValue("userName", userName);
43         }
44 
45         if (StringUtils.hasText(email)) {
46             builder.addPropertyValue("email", email);
47         }
48     }
49 }
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, 便可以使用自定义标签。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xmlns:myTag="http://www.itdoc.com/schema/user"
 5        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 6        http://www.itdoc.com/schema/user http://www.itdoc.com/schema/user.xsd">
 7 
 8     <myTag:user id="customBeanTest" userName="name" email="itdoc@163.com"/>
 9 
10 </beans>
View Code

 

7.测试

 1 /**
 2  * @filename: CustomMain.java
 3  * @author: Wang Chinda
 4  * @date: 2018-05-21
 5  * @version: v1.0
 6  * @modify_history: -
 7  * 20180521   Wang Chinda   create
 8  * 20180521   Wang Chinda   modify   method()
 9  */
10 package com.itdoc.learn.source.custom;
11 
12 import org.springframework.context.ApplicationContext;
13 import org.springframework.context.support.ClassPathXmlApplicationContext;
14 
15 /**
16  * @desc 测试
17  * @author Wang Chinda
18  * @create 2018-05-21 14:58
19  */
20 public class CustomMain {
21 
22     public static void main(String[] args) {
23         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test/customTest.xml");
24         User user = (User) applicationContext.getBean("customBeanTest");
25         System.out.println(user.getEmail() + " ---- " + user.getUserName());
26     }
27 }
View Code

 

控制台显示:

 

 

GitHub地址: https://github.com/wcd19901010/spring-01

 

posted @ 2018-05-21 17:33  Chinda  阅读(288)  评论(0编辑  收藏  举报