• 基于Spring的可扩展Schema进行开发自定义配置标签支持


    一、背景

      最近和朋友一起想开发一个类似alibaba dubbo的功能的工具,其中就用到了基于Spring的可扩展Schema进行开发自定义配置标签支持,通过上网查资料自己写了一个demo.今天在这里进行和大家分享,也记录下方便以后复习备忘。

    二、demo测试环境

      1.JDK1.7

      2.spring 4.2.5.RELEASE

      3.基于Maven

      4.开发工具Eclipse

    三、项目介绍

      1.实现步骤分析

        [1].设计配置属性并开发JavaBean.

        [2].编写xsd文件.

        [3].编写NamespaceHandler和BeanDefinitionParser完成解析工作.

        [4].编写spring.handlers和spring.schemas串联起所有部件.

        [5].编写名为application.xml的spring配置文件

        [6].Maven Java项目的pom.xml.

        [7].编写测试类进行测试.

      2.实现代码

        [1].设计配置属性并开发JavaBean代码(基于BaseBean进行定义)

    BaseBean.java

    1 package com.hafiz.zhang.tag.bean;
    2 
    3 public interface BaseBean {
    4     public abstract void init() throws Exception;
    5     public abstract void destory();
    6 }

    ApplicationBean.java

     1 package com.hafiz.zhang.tag.bean;
     2 
     3 public class ApplicationBean implements BaseBean {
     4     private String id;
     5     private String name;
     6     private String version;
     7     private String description;
     8     public String getId() {
     9         return id;
    10     }
    11     public void setId(String id) {
    12         this.id = id;
    13     }
    14     public String getVersion() {
    15         return version;
    16     }
    17     public void setVersion(String version) {
    18         this.version = version;
    19     }
    20     public String getName() {
    21         return name;
    22     }
    23     public void setName(String name) {
    24         this.name = name;
    25     }
    26     public String getDescription() {
    27         return description;
    28     }
    29     public void setDescription(String description) {
    30         this.description = description;
    31     }
    32 
    33     @Override
    34     public String toString() {
    35         return "ApplicationBean [id=" + id + ", name=" + name + ", version=" + version + ", description=" + description
    36                 + "]";
    37     }
    38     @Override
    39     public void init() throws Exception {
    40         System.out.println("---------ApplicationBean init---------");
    41     }
    42 
    43     @Override
    44     public void destory() {
    45         System.out.println("---------ApplicationBean destory---------");
    46     }
    47 
    48 }

      [2].编写xsd文件.

    hafiz.xsd(为上一步设计好的配置项编写XSD文件,XSD是schema的定义文件,配置的输入和解析输出都是以XSD为契约,本例中XSD如下:)

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <xsd:schema
     3     xmlns="http://www.hafiz.com/schema/hafiz"
     4     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     5     xmlns:beans="http://www.springframework.org/schema/beans"
     6     targetNamespace="http://www.hafiz.com/schema/hafiz"
     7     elementFormDefault="qualified"
     8     attributeFormDefault="unqualified">
     9     <xsd:import namespace="http://www.springframework.org/schema/beans"/>
    10     <xsd:element name="application">
    11         <xsd:complexType>
    12             <xsd:complexContent>
    13                 <xsd:extension base="beans:identifiedType">
    14                     <xsd:attribute name="name" type="xsd:string"/>
    15                     <xsd:attribute name="version" type="xsd:string"/>
    16                     <xsd:attribute name="description" type="xsd:string"/>
    17                 </xsd:extension>
    18             </xsd:complexContent>
    19         </xsd:complexType>
    20     </xsd:element>    
    21 </xsd:schema>

    注意:1.关于xsd:schema的各个属性具体含义就不作过多解释,可以参见http://www.w3school.com.cn/schema/schema_schema.asp.

       2.<xsd:element name="application">对应着配置项节点的名称,因此在应用中会用application作为节点名来引用这个配置.

       3.<xsd:attribute name="name" type="xsd:string" />和<xsd:attribute name="version" type="xsd:string" />以及

        <xsd:attribute name="description" type="xsd:string" />对应着配置项application的三个属性名,因此在应用中可以配置name和version以及description三个属性,都是string类型。

       4.完成后需把xsd存放在classpath下,一般都放在META-INF目录下(本例就放在这个目录下)  

      [3].编写NamespaceHandler和BeanDefinitionParser完成解析工作.

         下面需要完成解析工作,会用到NamespaceHandler和BeanDefinitionParser这两个概念。具体说来NamespaceHandler会根据schema和节点名找到某个BeanDefinitionParser,然后由BeanDefinitionParser完成具体的解析工作。因此需要分别完成NamespaceHandler和BeanDefinitionParser的实现类,Spring提供了默认实现类NamespaceHandlerSupport和BeanDefinitionParser,简单的方式就是去继承这两个类。本例就是采取这种方式:

    HafizNamespaceHandler.java

     1 package com.hafiz.zhang.tag.handlers;
     2 
     3 import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
     4 
     5 import com.hafiz.zhang.tag.bean.ApplicationBean;
     6 import com.hafiz.zhang.tag.parser.ApplicationBeanDefinitionParser;
     7 
     8 /**
     9  * @author hafiz.Zhang
    10  * @Date 2016年5月17日 下午12:22:57
    11  * @Description 定义自定义的命名空间hafiz处理器
    12  */
    13 public class HafizNamespaceHandler extends NamespaceHandlerSupport {
    14 
    15     @Override
    16     public void init() {
    17         //在这里进行注册自定义命名空间
    18         registerBeanDefinitionParser("application", new ApplicationBeanDefinitionParser(ApplicationBean.class));
    19     }
    20 
    21 }

    注:其中registerBeanDefinitionParser("application", new ApplicationBeanDefinitionParser());就是用来把节点名和解析类联系起来,在配置中引用application配置项时,就会用ApplicationBeanDefinitionParser来解析配置。

    ApplicationBeanDefinitionParser.java

     1 package com.hafiz.zhang.tag.parser;
     2 
     3 import org.springframework.beans.factory.config.BeanDefinition;
     4 import org.springframework.beans.factory.support.RootBeanDefinition;
     5 import org.springframework.beans.factory.xml.BeanDefinitionParser;
     6 import org.springframework.beans.factory.xml.ParserContext;
     7 import org.springframework.util.StringUtils;
     8 import org.w3c.dom.Element;
     9 
    10 /**
    11  * @author hafiz.Zhang
    12  * @Date 2016年5月17日 下午12:27:12
    13  * @Description 在这里定义自定义命名空间的bean解析器
    14  */
    15 public class ApplicationBeanDefinitionParser implements BeanDefinitionParser {
    16 
    17     private Class<?> clazz;
    18     public ApplicationBeanDefinitionParser(Class<?> clazz) {
    19         this.clazz = clazz;
    20     }
    21     @Override
    22     public BeanDefinition parse(Element element, ParserContext parserContext) {
    23         RootBeanDefinition rbd = new RootBeanDefinition();
    24         rbd.setBeanClass(clazz);
    25         String id = element.getAttribute("id");
    26         String name = element.getAttribute("name");
    27         String version = element.getAttribute("version");
    28         String desc = element.getAttribute("description");
    29         if(StringUtils.hasText(id)) {
    30             rbd.getPropertyValues().addPropertyValue("id", id);
    31         }
    32         if(StringUtils.hasText(name)) {
    33             parserContext.getRegistry().registerBeanDefinition(name, rbd);//这句话非常重要,意思是基于名字进行把对应的bean加载到spring容器中
    34             rbd.getPropertyValues().addPropertyValue("name", name);
    35         }
    36         if(StringUtils.hasText(version)) {
    37             rbd.getPropertyValues().addPropertyValue("version", version);
    38         }
    39         if(StringUtils.hasText(desc)) {
    40             rbd.getPropertyValues().addPropertyValue("description", desc);
    41         }
    42         rbd.setInitMethodName("init");
    43         rbd.setDestroyMethodName("destory");
    44         return rbd;
    45     }
    46 
    47 }

    注:其中element.getAttribute就是用配置中取得属性值,rbd.getPropertyValues().addPropertyValue就是把属性值放到bean中。

    [4].编写spring.handlers和spring.schemas串联起所有部件

      上面几个步骤走下来会发现开发好的handler与xsd还没法让spring容器感知到,就这样放上去是没法把前面做的工作纳入体系中的,spring提供了spring.handlers和spring.schemas这两个配置文件来完成这项工作,这两个文件需要我们自己编写并放入META-INF文件夹中,这两个文件的地址必须是META-INF/spring.handlers和META-INF/spring.schemas,spring会默认去载入它们,本例中spring.handlers如下所示:

     1 http://www.hafiz.com/schema/hafiz=com.hafiz.zhang.tag.handlers.HafizNamespaceHandler 

    以上表示当使用到名为"http://www.hafiz.com/schema/hafiz"的schema引用时,会通过com.hafiz.zhang.tag.handlers.HafizNamespaceHandler来完成解析.

    spring.schemas如下所示:

     1 http://www.hafiz.com/schema/hafiz.xsd=META-INF/hafiz.xsd 

    以上就是载入xsd文件。

    注:以上两个文件中的""表示转义。 

    [5].编写名为application.xml的spring配置文件

      编写application.xml文件并放在classpath下,建议这样做,但不是必须放在该位置。

      

     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:hafiz="http://www.hafiz.com/schema/hafiz"
     5     xsi:schemaLocation="  
     6         http://www.springframework.org/schema/beans 
     7         http://www.springframework.org/schema/beans/spring-beans-4.2.xsd  
     8         http://www.hafiz.com/schema/hafiz 
     9         http://www.hafiz.com/schema/hafiz.xsd">
    10 
    11     <hafiz:application id="test_demo" name="appliationBean" version="1.1.0" description="这是我自动拓展spring的schema的测试demo"/>
    12 
    13 </beans>

    其中xmlns:hafiz="http://www.hafiz.com/schema/hafiz"是用来指定自定义schema,xsi:schemaLocation用来指定xsd文件。

    <hafiz:application id="test_demo" name="applicationBean" version="1.1.0" description="这是我自动拓展spring的schema的测试demo"/>是一个具体的自定义配置使用实例.

    注:此处的hafiz不是不能改变的,只要使用和上面指定的“xmlns:标签名”的标签名一样就可以(代码加粗标黑处)

    [6].Maven Java项目的pom.xml.

    在该文件中主要引入spring的依赖

     1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     2   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     3   <modelVersion>4.0.0</modelVersion>
     4 
     5   <groupId>com.hafiz.zhang</groupId>
     6   <artifactId>springTag</artifactId>
     7   <version>0.0.1-SNAPSHOT</version>
     8   <packaging>jar</packaging>
     9 
    10   <name>springTag</name>
    11   <url>http://maven.apache.org</url>
    12 
    13   <properties>
    14     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    15     <spring.version>4.2.5.RELEASE</spring.version>
    16   </properties>
    17 
    18   <dependencies>
    19     <dependency>
    20       <groupId>junit</groupId>
    21       <artifactId>junit</artifactId>
    22       <version>3.8.1</version>
    23       <scope>test</scope>
    24     </dependency>
    25     <!-- springframework start -->
    26     <dependency>
    27         <groupId>org.springframework</groupId>
    28         <artifactId>spring-core</artifactId>
    29         <version>${spring.version}</version>
    30     </dependency>
    31     <dependency>
    32         <groupId>org.springframework</groupId>
    33         <artifactId>spring-context-support</artifactId>
    34         <version>${spring.version}</version>
    35     </dependency>
    36     <!-- springframe end -->
    37   </dependencies>
    38 </project>

    [7].编写测试类进行测试.

     1 package com.hafiz.zhang.test;
     2 
     3 import org.springframework.context.ApplicationContext;
     4 import org.springframework.context.support.ClassPathXmlApplicationContext;
     5 
     6 import com.hafiz.zhang.tag.bean.ApplicationBean;
     7 
     8 /**
     9  * @author hafiz.Zhang
    10  * @Date 2016年5月17日 下午2:01:37
    11  * @Description 在此类中进行测试自定义拓展的schema
    12  */
    13 public class ApplicationTest 
    14 {
    15     private static ApplicationContext ac;
    16     public static void main( String[] args )
    17     {
    18         ac = new ClassPathXmlApplicationContext("application.xml");
    19         ApplicationBean bean = (ApplicationBean)ac.getBean("appliationBean");
    20         System.out.println( "配置文件中的bean为:" );
    21         System.out.println( "id = " + bean.getId() );
    22         System.out.println( "name = " + bean.getName() );
    23         System.out.println( "version = " + bean.getVersion() );
    24         System.out.println( "description = " + bean.getDescription() );
    25     }
    26 }

    在控制台会输出:

    附:项目结构图

    到此为止,spring的自定义标签就已经实现了,欢迎大家进行交流学习~

  • 相关阅读:
    北邮ivi测试频道 26个高清频道 IPv4 有PC端和移动端地址
    Nginx+ffmpeg+ckplayer海康监控摄像头在web页面播放RTSP转RTMP
    Vlc播放RTSP
    [RTSP]WPF用VLC显示RTSP视频
    WPF 使用 VLC 3.0.10 的基本操作
    【矩阵专题】——矩阵快速幂
    1120:同行列对角线的格
    1120:同行列对角线的格
    1120:同行列对角线的格
    1119:矩阵交换行
  • 原文地址:https://www.cnblogs.com/hafiz/p/5502562.html
Copyright © 2020-2023  润新知