从Spring2.0开始,Spring提供了XML Schema可扩展机制,用户可以自定义XML Schema文件,并自定义XML Bean解析器,并集成到Spring Ioc 容器中
完成XML自定义扩展,需要下面几个步骤:
创建一个 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 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
可以注册任意个BeanDefinitionParser
。NamespaceHandlerSupport
负责所有自定义元素的编排,而解析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实现自定义标签
完成以上工作的话,那么我们就可以使用我们自定义的标签了,接下来我们来看详细的例子实现。
创建pojo类
- package net.itaem.vo;
- public class User {
- private String name;
- private String sex;
- private String email;
- private String id;
- //set get method...
- }
定义一个xsd文件描述组件类容
- <?xml version="1.0" encoding="UTF-8"?>
- <schema xmlns="http://www.w3.org/2001/XMLSchema"
- targetNamespace="http://www.lexueba.com/schema/user"
- xmlns:tns="http://www.lexueba.com/schema/user"
- elementFormDefault="qualified">
- <element name="user">
- <complexType>
- <attribute name="id" type="string" />
- <attribute name="name" type="string" />
- <attribute name="sex" type="string" />
- <attribute name="email" type="string" />
- </complexType>
- </element>
- </schema>
实现AbstractSingleBeanDefinitionParser接口
- /*
- * Copyright 2002-2013 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package net.itaem.parser;
- import net.itaem.vo.User;
- import org.springframework.beans.factory.support.BeanDefinitionBuilder;
- import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
- import org.springframework.util.StringUtils;
- import org.w3c.dom.Element;
- /**
- *
- * @author Administrator
- */
- public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser{
- /* (non-Javadoc)
- * @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
- */
- @Override
- protected Class<?> getBeanClass(Element element) {
- return User.class;
- }
- //从elelment中解析并提取对应的元素
- @Override
- protected void doParse(Element element, BeanDefinitionBuilder builder) {
- String name=element.getAttribute("name");
- String email=element.getAttribute("sex");
- String sex=element.getAttribute("sex");
- //将提取到的数据放入beanDefinitionBuilder 中,待完成所有的bean解析后统一放到beanfactory
- if(StringUtils.hasText(name)){
- builder.addPropertyValue("name", name);
- }
- if(StringUtils.hasText(email)){
- builder.addPropertyValue("email", email);
- }
- if(StringUtils.hasText(sex)){
- builder.addPropertyValue("sex", sex);
- }
- }
- }
继承抽象类NamespaceHandlerSupport
- /*
- * Copyright 2002-2013 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package net.itaem.handler;
- import net.itaem.parser.UserBeanDefinitionParser;
- import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
- /**
- *
- * @author Administrator
- */
- public class MyNamespaceHandler extends NamespaceHandlerSupport {
- /* (non-Javadoc)
- * @see org.springframework.beans.factory.xml.NamespaceHandler#init()
- */
- @Override
- public void init() {
- registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
- }
- }
修改(添加)spring.handlers文件和spring.schemas文件
修改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)
创建测试配置文件
- <?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:myname="http://www.lexueba.com/schema/user"
- xsi:schemaLocation="
- http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.lexueba.com/schema/user http://www.lexueba.com/schema/user.xsd">
- <myname:user id="beantest" name="whx" email="494863082@qq.com" sex="男"/>
- </beans>
编写测试代码
- package net.itaem.test;
- import net.itaem.vo.User;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class Test {
- public static void main(String[] args) {
- ApplicationContext context=new ClassPathXmlApplicationContext("net/itaem/source/custom_user.xml");
- User bt=(User) context.getBean("beantest");
- System.out.println(bt);
- }
- }
运行结果可以看到,程序正常输出bean的信息