一、背景

  最近和朋友一起想开发一个类似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

 package com.hafiz.zhang.tag.bean;

 public interface BaseBean {
public abstract void init() throws Exception;
public abstract void destory();
}

ApplicationBean.java

 package com.hafiz.zhang.tag.bean;

 public class ApplicationBean implements BaseBean {
private String id;
private String name;
private String version;
private String description;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
} @Override
public String toString() {
return "ApplicationBean [id=" + id + ", name=" + name + ", version=" + version + ", description=" + description
+ "]";
}
@Override
public void init() throws Exception {
System.out.println("---------ApplicationBean init---------");
} @Override
public void destory() {
System.out.println("---------ApplicationBean destory---------");
} }

  [2].编写xsd文件.

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

 <?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns="http://www.hafiz.com/schema/hafiz"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://www.hafiz.com/schema/hafiz"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:element name="application">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="version" type="xsd:string"/>
<xsd:attribute name="description" type="xsd:string"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</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

 package com.hafiz.zhang.tag.handlers;

 import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

 import com.hafiz.zhang.tag.bean.ApplicationBean;
import com.hafiz.zhang.tag.parser.ApplicationBeanDefinitionParser; /**
* @author hafiz.Zhang
* @Date 2016年5月17日 下午12:22:57
* @Description 定义自定义的命名空间hafiz处理器
*/
public class HafizNamespaceHandler extends NamespaceHandlerSupport { @Override
public void init() {
//在这里进行注册自定义命名空间
registerBeanDefinitionParser("application", new ApplicationBeanDefinitionParser(ApplicationBean.class));
} }

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

ApplicationBeanDefinitionParser.java

 package com.hafiz.zhang.tag.parser;

 import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element; /**
* @author hafiz.Zhang
* @Date 2016年5月17日 下午12:27:12
* @Description 在这里定义自定义命名空间的bean解析器
*/
public class ApplicationBeanDefinitionParser implements BeanDefinitionParser { private Class<?> clazz;
public ApplicationBeanDefinitionParser(Class<?> clazz) {
this.clazz = clazz;
}
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition rbd = new RootBeanDefinition();
rbd.setBeanClass(clazz);
String id = element.getAttribute("id");
String name = element.getAttribute("name");
String version = element.getAttribute("version");
String desc = element.getAttribute("description");
if(StringUtils.hasText(id)) {
rbd.getPropertyValues().addPropertyValue("id", id);
}
if(StringUtils.hasText(name)) {
parserContext.getRegistry().registerBeanDefinition(name, rbd);//这句话非常重要,意思是基于名字进行把对应的bean加载到spring容器中
rbd.getPropertyValues().addPropertyValue("name", name);
}
if(StringUtils.hasText(version)) {
rbd.getPropertyValues().addPropertyValue("version", version);
}
if(StringUtils.hasText(desc)) {
rbd.getPropertyValues().addPropertyValue("description", desc);
}
rbd.setInitMethodName("init");
rbd.setDestroyMethodName("destory");
return rbd;
} }

注:其中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如下所示:

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如下所示:

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

以上就是载入xsd文件。

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

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

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

  

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

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.hafiz.zhang</groupId>
<artifactId>springTag</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>springTag</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.2.5.RELEASE</spring.version>
</properties> <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- springframework start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- springframe end -->
</dependencies>
</project>

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

 package com.hafiz.zhang.test;

 import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.hafiz.zhang.tag.bean.ApplicationBean; /**
* @author hafiz.Zhang
* @Date 2016年5月17日 下午2:01:37
* @Description 在此类中进行测试自定义拓展的schema
*/
public class ApplicationTest
{
private static ApplicationContext ac;
public static void main( String[] args )
{
ac = new ClassPathXmlApplicationContext("application.xml");
ApplicationBean bean = (ApplicationBean)ac.getBean("appliationBean");
System.out.println( "配置文件中的bean为:" );
System.out.println( "id = " + bean.getId() );
System.out.println( "name = " + bean.getName() );
System.out.println( "version = " + bean.getVersion() );
System.out.println( "description = " + bean.getDescription() );
}
}

在控制台会输出:

附:项目结构图

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

基于Spring的可扩展Schema进行开发自定义配置标签支持的更多相关文章

  1. Spring源码阅读笔记05:自定义xml标签解析

    在上篇文章中,提到了在Spring中存在默认标签与自定义标签两种,并且详细分析了默认标签的解析,本文就来分析自定义标签的解析,像Spring中的AOP就是通过自定义标签来进行配置的,这里也是为后面学习 ...

  2. 基于Spring的最简单的定时任务实现与配置(一)

    朋友的项目中有点问题.他那边是Spring架构的,有一个比较简单的需要定时的任务执行.在了解了他的需求之后,于是提出了比较简单的Spring+quartz的实现方式. 注意本文只是讨论,在已搭建完毕的 ...

  3. 基于Spring的最简单的定时任务实现与配置(二)

    接上一篇,原本我以为我实现的方式很简单了,在准备写(一)的时候,就去查了查别人是怎么实现定时任务的.不查还好,这一查,发现还有更简单的.所以就会有这篇文章. 本文主要是讨论,在完成Spring 项目搭 ...

  4. 基于spring的aop实现读写分离与事务配置

    项目开发中经常会遇到读写分离等多数据源配置的需求,在Java项目中可以通过Spring AOP来实现多数据源的切换. 一.Spring事务开启流程 Spring中通常通过@Transactional来 ...

  5. 基于Spring的最简单的定时任务实现与配置(三)--番外篇 cron表达式的相关内容

    本来这篇文章是会跟本系列的前两篇文章一起发布的.但是,昨天在找资料总结的时候遇到了一点意外,就延后了一些. 本篇的内容主要参考了 这篇博文:http://www.cnblogs.com/junrong ...

  6. JMeter扩展插件实现对自定义协议进行支持 转

    本文版权归xmeter.net 所有.欢迎转载,转载请注明出处. 摘要## JMeter本身提供了插件机制,允许第三方扩展JMeter以支持JMeter不支持的协议的测试.本文以扩展一个简单的Apac ...

  7. C#如何使用和开发自定义配置节

    在日常的程序设计中,如何灵活和巧妙地运用配置信息是一个成功的设计师的首要选择.这不仅是为了程序设计得更灵活性和可扩展性,也是为了让你的代码给人以清新的感觉.程序中的配置信息一般放在应用程序的app.c ...

  8. pythonDjango开发-自定义模板标签

    1.在APP同级目录新建文件夹'templatetags' 并在文件夹下创建__init__.py和定义标签用的customer.py文件 2.在customer.py文件中定义自定义标签 from ...

  9. Spring可扩展Schema标签

    基于Spring可扩展Schema提供自己定义配置支持 http://blog.csdn.net/cutesource/article/details/5864562 WARN : org.sprin ...

随机推荐

  1. [转]C++模板学习

    1. 模板的概念. 我们已经学过重载(Overloading),对重载函数而言,C++的检查机制能通过函数参数的不同及所属类的不同.正确的调用重载函数.例如,为求两个数的最大值,我们定义MAX()函数 ...

  2. maven 向本地私库导入jar

    mvn install:install-file -DgroupId=<your_group_name> -DartifactId=<your_artifact_name> - ...

  3. OC第三节——NSArray和NSMutableArray

    1.OC数组和c数组的区别        C语言的数组:            相同类型变量的有序结合. 类型:可以是简答数据类型.构造数据类型                int     a[10 ...

  4. win7下搭建web服务器

    简介 微软为了操作系统的安全,默认把web.ftp等服务默认关闭了,重新打开也非常简单. step 1 打开控制面板: step 2: step 3: step 4: 单击确定之后等个几分钟,web服 ...

  5. MySQL Python教程(4)

    Class cursor.MySQLCursorBuffered 该类从Class cursor.MySQLCursorBuffered继承,如果需要,可以在执行完SQL语句后自动缓冲结果集合.imp ...

  6. jQuery源码笔记(一):jQuery的整体结构

    jQuery 是一个非常优秀的 JS 库,与 Prototype,YUI,Mootools 等众多的 Js 类库相比,它剑走偏锋,从 web 开发的实用角度出发,抛除了其它 Lib 中一些中看但不实用 ...

  7. [BZOJ1503][NOI2004]郁闷的出纳员

    [BZOJ1503][NOI2004]郁闷的出纳员 试题描述 OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作,但是 ...

  8. OOP感想

    OOP是面向对象编程(Object Oriented Programming).集于一身,最终目的是各司其职,让每个职责的只关注自己那块,其他的不管丢给下一个人.比如说,一个页面,对于客户,只要能看到 ...

  9. Opencv CamShift+Kalman目标跟踪

    #include "stdio.h" #include "string.h" #include "iostream" #include &q ...

  10. rest api设计的一般原则

    本文参考自:http://www.ruanyifeng.com/blog/2014/05/restful_api.html,http://www.dongming8.cn/?p=590 服务器端: 1 ...