Spring——可扩展的XML Schema机制
前言
从Spring2.0开始,Spring提供了XML Schema可扩展机制,用户可以自定义XML Schema文件,并自定义XML Bean解析器,并集成到Spring Ioc 容器中。
完成XML自定义扩展,需要下面几个步骤:
-
创建一个 XML Schema 文件,描述自定义的合法构建模块,也就是xsd文件
-
自定义个处理器类,并实现
NamespaceHandler
接口 -
自定义一个或多个解析器,实现
BeanDefinitionParser
接口 -
注册NamespaceHandler和 Schema
按照上面的步骤,实现如下可扩展XML元素:
<myns:dateformat id="dateFormat" pattern="yyyy-MM-dd HH:mm" />
一、自定义 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
二、自定义 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
可以注册任意个BeanDefinitionParser
。NamespaceHandlerSupport
负责所有自定义元素的编排,而解析XML的工作委托给各个BeanDefinitionParser
负责。三、自定义BeanDefinitionParser
BeanDefinitionParser
将被调用,如果NamespapceHandler
遇到元素类型已经有对应注册的parser(例如上面的handler如果遇到dateformat,DateformatDefinitionParser
会被调用,解析相应的属性设置到Bean中)将会被调用。BeanDefinitionParser
负责解析一个顶级元素。 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); } }
四、注册handler和schema
为了让Spring在解析xml的时候能够感知到我们的自定义元素,我们需要把namespaceHandler
和xsd文件放到2个指定的配置文件中,
这2个文件都位于META-INF
目录中。
4.1 spring.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.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(); } } }
Dubbo XML Schema 扩展实现
Dubbo 配置:
我们查看 Dubbo XML Schema 扩展如何实现:
可以看到 Dubbo XML Schema 扩展刚好对应 Spring 四个标准的步骤。