Spring可扩展的XML Schema机制(spring实现自定义标签)

从Spring2.0开始,Spring提供了XML Schema可扩展机制,用户可以自定义XML Schema文件,并自定义XML Bean解析器,并集成到Spring Ioc 容器中

完成XML自定义扩展,需要下面几个步骤:



可扩展XML Schema机制.png
  • 创建一个 XML Schema 文件,描述自定义的合法构建模块,也就是xsd文件

  • 自定义个处理器类,并实现NamespaceHandler接口(比较容易)

  • 自定义一个或多个解析器,实现BeanDefinitionParser接口(最关键的部分)

  • 注册上面的组件到Spring IOC容器中

按照上面的步骤,实现如下可扩展XML元素


  <myns:dateformat id="dateFormat" pattern="yyyy-MM-dd HH:mm" />

可通过这种方式定义SimpleDateFormat

类似


<bean id="dateFormat" class="java.text.SimpleDateFormat"> 

    <constructor-arg value="yyyy-HH-dd HH:mm"/>

</bean>

1.自定义 XML Schema 文件

XML Schema 介绍


<?xml version="1.0" encoding="UTF-8"?>

<xsd:schema xmlns="http://www.lh.com/schema/myns" 

    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 

    xmlns:beans="http://www.springframework.org/schema/beans" 

    targetNamespace="http://www.lh.com/schema/myns" 

    elementFormDefault="qualified" attributeFormDefault="unqualified"> 

    <xsd:import namespace="http://www.springframework.org/schema/beans"/> 

    <xsd:element name="dateformat">     

        <xsd:complexType>        

         <xsd:complexContent>        

               <xsd:extension base="beans:identifiedType">

                 <xsd:attribute name="pattern" type="xsd:string" use="required"/>

              </xsd:extension>

         </xsd:complexContent>

       </xsd:complexType>

    </xsd:element>

</xsd:schema>

其中自定义的命名空间是http://www.lh.com/schema/myns,定义了dateformat元素,属性有pattern

2.自定义 NamespaceHandler

定义好XML Schema文件,需要定义一个NamespaceHandler解析配置文件。

NamespaceHandler接口是非常简单的,只有三个方法:

  • init(): NamespaceHandler被使用之前调用,完成NamespaceHandler的初始化

  • BeanDefinition parse(Element, ParserContext): 当遇到顶层元素时被调用

  • BeanDefinition decorate(Node,BeanDefinitionHandler,ParserContext): 当遇到一个属性或者嵌套元素的时候调用

Spring提供了一个默认的实现类NamespaceHandlerSupport,我们只需要在init的时候注册每个元素的解析器即可。


public class DateformatNamespaceHandler extends NamespaceHandlerSupport { 

    public void init() { 

        registerBeanDefinitionParser("dateformat", new DeteformatDefinitionParser()); 

    }

}

这里实际用到了代理委托的概念,NamespaceHandlerSupport可以注册任意个BeanDefinitionParserNamespaceHandlerSupport负责所有自定义元素的编排,而解析XML的工作委托给各个BeanDefinitionParser负责。

3.自定义BeanDefinitionParser

BeanDefinitionParser 将被调用,如果NamespapceHandler遇到元素类型已经有对应注册的parser(例如上面的handler如果遇到dateformat,DateformatDefinitionParser会被调用,解析相应的属性设置到Bean中)将会被调用。BeanDefinitionParser负责解析一个顶级元素。

Spring提供了AbstractSingleBeanDefinitionParser来处理繁重的解析工作,只需要实现两个方法:

  • Class<?> getBeanClass(Element):返回元素的Class类型

  • void doParse(Element element,BeanDefinitionBuilder builder):添加元素的属性或者构造参数等等


package com.lh.spring;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;

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

import org.w3c.dom.Element;import java.text.SimpleDateFormat;

public class DateformatDefinitionParser extends AbstractSingleBeanDefinitionParser { 

    @Override 

    protected Class<?> getBeanClass(Element element) { 

        return SimpleDateFormat.class; 

    } 

    @Override

    protected void doParse(Element element, BeanDefinitionBuilder builder) { 

        String pattern = element.getAttribute("pattern"); 

        builder.addConstructorArgValue(pattern); 

    }

}

4.注册handler和schema

为了让Spring在解析xml的时候能够感知到我们的自定义元素,我们需要把namespaceHandler和xsd文件放到2个指定的配置文件中,这2个文件都位于META-INF目录中

4.1spring.handlers文件包含了xml schema uri 和 Handler类的映射关系,例如


http\://www.lh.com/schema/myns=com.lh.spring.DateformatNamespaceHandler

遇到http://www.lh.com/schema/myns名空间的时候会交给CarNamespaceHandler来处理,key部分必须和xsd文件中的targetNamespace值保持一致

4.2 spring.schemas文件包含了xml schema xsd文件命名空间和文件路径的映射关系,例如


http\://www.lh.com/schema/myns.xsd=META-INF/com.lh.date.format/sfm-1.0.xsd

5.实践测试

5.1 加入Spring配置文件:


<?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:my="http://www.lh.com/schema/myns" 

           xsi:schemaLocation=" http://www.springframework.org/schema/beans

                                http://www.springframework.org/schema/beans/spring-beans.xsd

                                http://www.lh.com/schema/myns 

                                http://www.lh.com/schema/myns.xsd"> 

    <my:dateformat id="dateformat" pattern="yyyy-MM-dd HH:mm" />

</beans>

5.2 单元测试:

全部代码


package com.lh.spring;

import org.junit.Assert;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Date;

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations = {"classpath:spring/application-core.xml"})

public class XMLSchemaCustomTest { 

    @Autowired private SimpleDateFormat dateFormat;

    @Test 

    public void propertyTest() { 

        Assert.assertNotNull(dateFormat);

        String date = "2010-10-10 11:12:14";

        SimpleDateFormat ymdhms = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 

        try { 

            Date time = ymdhms.parse(date); 

            String dateFormatStr = dateFormat.format(time); 

            Assert.assertTrue("2010-10-10 11:12".equals(dateFormatStr));

        } catch (ParseException e) { 

            e.printStackTrace(); 

        } 

    }

}
转载自: Spring可扩展的XML Schema机制

spring实现自定义标签

首先我们来理一理如何自定义一个spring的标签,像bean标签那样使用,我们先概览一些整体的流程:

1)创建一个需要扩展的组件
2)定义一个XSD文件,描述组件内容
3)创建一个java类,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义
4)创建一个Handler类,扩展子NameSpaceHandlerSupport,目的是将组件注册到容器。
5)编写(添加)Spring.handlers和Spring.schemas文件。

完成以上工作的话,那么我们就可以使用我们自定义的标签了,接下来我们来看详细的例子实现。

创建pojo类

[java]  view plain  copy
  1. package net.itaem.vo;  
  2.   
  3. public class User {  
  4.   
  5.     private String name;  
  6.     private String sex;  
  7.     private String email;  
  8.     private String id;  
  9.       
  10.     //set get method...  
  11.       
  12. }  


定义一个xsd文件描述组件类容

以前spring使用的是dtd文件,现在几乎使用的都是xsd文件了,xsd文件的定义如果不了解的朋友可以去 http://www.phpstudy.net/e/schema/  这个网址看看,用法比较简单,这里就不介绍如何使用了,下面是按照schema的规则定义一个xsd文件

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <schema xmlns="http://www.w3.org/2001/XMLSchema"  
  3.             targetNamespace="http://www.lexueba.com/schema/user"  
  4.             xmlns:tns="http://www.lexueba.com/schema/user"  
  5.             elementFormDefault="qualified">  
  6.   
  7.   
  8. <element name="user">  
  9.     <complexType>  
  10.         <attribute name="id" type="string" />  
  11.         <attribute name="name" type="string" />  
  12.         <attribute name="sex" type="string" />  
  13.         <attribute name="email" type="string" />  
  14.     </complexType>  
  15. </element>  
  16.   
  17. </schema>  


实现AbstractSingleBeanDefinitionParser接口

实现了这个接口,当spring加载文档的时候,遇到你定义的标签,他就会回调你的这个解析方法,进行你自定义的属性解析。

[java]  view plain  copy
  1. /* 
  2.  * Copyright 2002-2013 the original author or authors. 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  * http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package net.itaem.parser;  
  18.   
  19. import net.itaem.vo.User;  
  20.   
  21. import org.springframework.beans.factory.support.BeanDefinitionBuilder;  
  22. import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;  
  23. import org.springframework.util.StringUtils;  
  24. import org.w3c.dom.Element;  
  25.   
  26.   
  27. /** 
  28.  *  
  29.  * @author Administrator 
  30.  */  
  31. public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser{  
  32.   
  33.     /* (non-Javadoc) 
  34.      * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element) 
  35.      */  
  36.     @Override  
  37.     protected Class<?> getBeanClass(Element element) {  
  38.         return  User.class;  
  39.     }  
  40.       
  41.     //从elelment中解析并提取对应的元素  
  42.     @Override  
  43.     protected void doParse(Element element, BeanDefinitionBuilder builder) {  
  44.        
  45.         String name=element.getAttribute("name");  
  46.         String email=element.getAttribute("sex");  
  47.         String sex=element.getAttribute("sex");  
  48.         //将提取到的数据放入beanDefinitionBuilder 中,待完成所有的bean解析后统一放到beanfactory  
  49.         if(StringUtils.hasText(name)){  
  50.             builder.addPropertyValue("name", name);  
  51.         }  
  52.         if(StringUtils.hasText(email)){  
  53.             builder.addPropertyValue("email", email);  
  54.         }  
  55.         if(StringUtils.hasText(sex)){  
  56.             builder.addPropertyValue("sex", sex);  
  57.         }  
  58.           
  59.     }  
  60.        
  61. }  


继承抽象类NamespaceHandlerSupport

定义了标签,这里就是标签的处理器,处理器中会把解析类(UserBeanDefinitionParser)的实例传入spring中,使得当spring解析到该标签的时候可以回调该实例的方法。

[java]  view plain  copy
  1. /* 
  2.  * Copyright 2002-2013 the original author or authors. 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  * http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package net.itaem.handler;  
  18.   
  19. import net.itaem.parser.UserBeanDefinitionParser;  
  20.   
  21. import org.springframework.beans.factory.xml.NamespaceHandlerSupport;  
  22.   
  23.   
  24. /** 
  25.  *  
  26.  * @author Administrator 
  27.  */  
  28. public class MyNamespaceHandler extends NamespaceHandlerSupport {  
  29.   
  30.     /* (non-Javadoc) 
  31.      * @see org.springframework.beans.factory.xml.NamespaceHandler#init() 
  32.      */  
  33.     @Override  
  34.     public void init() {  
  35.         registerBeanDefinitionParser("user"new UserBeanDefinitionParser());  
  36.     }  
  37.   
  38. }  


修改(添加)spring.handlers文件和spring.schemas文件

这两个文件默认路径是在META-INF这个路径下面

修改spring.handlers 文件,添加以下内容

http\://www.lexueba.com/schema/user(自定义的)=net.itaem.handler.MyNamespaceHandler

修改spring.schemas文件的内容,添加以下内容

http\://www.lexueba.com/schema/user.xsd(自定义的)=org/springframework/beans/factory/xml/use.xsd

修改好之后在使用自定义标签的时候便会把这些加到xsi:schemaLocation里面去,然后spring会自己去找xsd文件以及处理器(handler)


创建测试配置文件

使用的时候可以加上自己定义的命名空间,然后再xsi:schemaLocation加上对应的内容,就可以使用自己定义的标签了。

[html]  view plain  copy
  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:myname="http://www.lexueba.com/schema/user"  
  5.   
  6.     xsi:schemaLocation="  
  7.         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
  8.         http://www.lexueba.com/schema/user http://www.lexueba.com/schema/user.xsd">  
  9.   
  10.      <myname:user id="beantest" name="whx" email="494863082@qq.com" sex="男"/>   
  11. </beans>  

编写测试代码

[java]  view plain  copy
  1. package net.itaem.test;  
  2.   
  3. import net.itaem.vo.User;  
  4.   
  5. import org.springframework.context.ApplicationContext;  
  6. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  7.   
  8. public class Test {  
  9.       
  10.     public static void main(String[] args) {  
  11.         ApplicationContext context=new ClassPathXmlApplicationContext("net/itaem/source/custom_user.xml");  
  12.         User bt=(User) context.getBean("beantest");  
  13.         System.out.println(bt);  
  14.     }  
  15.        
  16. }  

运行结果可以看到,程序正常输出bean的信息


总结

自定义spring的标签,由于spring为我们做了大量的封装,自定义起来总体来说还是比较简单的,后续我们会看到,其实spring的aop的标签,类似这些非spring默认的标签,这些都是需要经过这些流程去实现自定义标签的。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值