• 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

  • 相关阅读:
    图形化编程娱乐于教,Kittenblock实例,角色对话
    图形化编程娱乐于教,Kittenblock实例,角色旋转方式
    图形化编程娱乐于教,Kittenblock实例,蝙蝠侠在宇宙中的坐标位置
    图形化编程娱乐于教,Kittenblock实例,角色移动,面向方向
    图形化编程娱乐于教,Kittenblock实例,角色移动
    图形化编程娱乐于教,Kittenblock实例,确认坐标
    图形化编程娱乐于教,Kittenblock实例,计算跑步距离
    图形化编程娱乐于教,Kittenblock实例,角色上跳效果
    图形化编程娱乐于教,Kittenblock实例,提问机器
    图形化编程娱乐于教,Kittenblock实例,数数,说出5的倍数
  • 原文地址:https://www.cnblogs.com/chinda/p/9068373.html
Copyright © 2020-2023  润新知