看了spring test 用例,其实实现这一功能还算比较简单,主要分以下的步骤,具体的实例可以去参考spring 自带的testcase
首先定义相关xsd文件,用于验证相应的行为:
主要增加了4个自定义元素和1个属性:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.springframework.org/schema/beans/test"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.springframework.org/schema/beans/test"
elementFormDefault="qualified">
<xsd:element name="testBean">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string" use="required" form="unqualified"/>
<xsd:attribute name="name" type="xsd:string" use="required" form="unqualified"/>
<xsd:attribute name="age" type="xsd:integer" use="required" form="unqualified"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="set">
<xsd:complexType>
<xsd:attribute name="name" type="xsd:string" use="required" form="unqualified"/>
<xsd:attribute name="age" type="xsd:integer" use="required" form="unqualified"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="debug"/>
<xsd:element name="nop"/>
<xsd:attribute name="object-name" type="xsd:string"/>
</xsd:schema>
接着定义handler映射文件:customNamespace.properties
http/://www.springframework.org/schema/beans/test=org.springframework.beans.factory.xml.support.TestNamespaceHandler
定义Handler:
主要注册相应的解析类和装饰类
publicclass TestNamespaceHandler extends NamespaceHandlerSupport {
publicvoid init() {
//相对于每个xsd中定义的元素
registerBeanDefinitionParser("testBean", new TestBeanDefinitionParser());
registerBeanDefinitionDecorator("set", new PropertyModifyingBeanDefinitionDecorator());
registerBeanDefinitionDecorator("debug", new DebugBeanDefinitionDecorator());
registerBeanDefinitionDecorator("nop", new NopInterceptorBeanDefinitionDecorator());
registerBeanDefinitionDecoratorForAttribute("object-name", new ObjectNameBeanDefinitionDecorator());
}
}
定义各个解析类:
privatestaticclass TestBeanDefinitionParser implements BeanDefinitionParser {
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition definition = new RootBeanDefinition();
definition.setBeanClass(TestBean.class);
MutablePropertyValues mpvs = new MutablePropertyValues();
mpvs.addPropertyValue("name", element.getAttribute("name"));
mpvs.addPropertyValue("age", element.getAttribute("age"));
definition.setPropertyValues(mpvs);
parserContext.getRegistry().registerBeanDefinition(element.getAttribute("id"), definition);
returnnull;
}
}
privatestaticclassPropertyModifyingBeanDefinitionDecorator implements BeanDefinitionDecorator {
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition,
ParserContext parserContext) {
Element element = (Element)node;
BeanDefinition def = definition.getBeanDefinition();
MutablePropertyValues mpvs = (def.getPropertyValues() == null) ?
new MutablePropertyValues() : def.getPropertyValues();
mpvs.addPropertyValue("name", element.getAttribute("name"));
mpvs.addPropertyValue("age", element.getAttribute("age"));
((AbstractBeanDefinition) def).setPropertyValues(mpvs);
return definition;
}
}
privatestaticclassDebugBeanDefinitionDecorator extends AbstractInterceptorDrivenBeanDefinitionDecorator {
protected BeanDefinition createInterceptorDefinition(Node node) {
returnnew RootBeanDefinition(DebugInterceptor.class);
}
}
privatestaticclassNopInterceptorBeanDefinitionDecorator extends
AbstractInterceptorDrivenBeanDefinitionDecorator {
protected BeanDefinition createInterceptorDefinition(Node node) {
returnnew RootBeanDefinition(NopInterceptor.class);
}
}
privatestaticclassObjectNameBeanDefinitionDecorator implements BeanDefinitionDecorator {
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition,
ParserContext parserContext) {
Attr objectNameAttribute = (Attr)node;
definition.getBeanDefinition().setAttribute("objectName", objectNameAttribute.getValue());
return definition;
}
}
可以定义EntityResolver,用于验证相应的xsd
privateclass DummySchemaResolver extends PluggableSchemaResolver {
public DummySchemaResolver() {
super(CustomNamespaceHandlerTests.this.getClass().getClassLoader());
}
public InputSource resolveEntity(String publicId, String systemId) throws IOException {
InputSource source = super.resolveEntity(publicId, systemId);
if (source == null) {
Resource resource =
new ClassPathResource("org/springframework/beans/factory/xml/support/spring-test.xsd");
source = new InputSource(resource.getInputStream());
source.setPublicId(publicId);
source.setSystemId(systemId);
}
return source;
}
}
关键的一步,如何生效:
String location = "org/springframework/beans/factory/xml/support/customNamespace.properties";
NamespaceHandlerResolver resolver = new DefaultNamespaceHandlerResolver(
getClass().getClassLoader(), location);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.setNamespaceHandlerResolver(resolver);
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
reader.setEntityResolver(new DummySchemaResolver());
reader.loadBeanDefinitions(getResource());
写一个测试xml文件:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:test="http://www.springframework.org/schema/beans/test"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
<test:testBeanid="testBean"name="Rob Harrop"age="23"/>
<beanid="customisedTestBean"class="org.springframework.beans.TestBean">
<test:setname="Rob Harrop"age="23"/>
</bean>
<beanid="debuggingTestBean"class="org.springframework.beans.TestBean">
<test:debug/>
<propertyname="name"value="Rob Harrop"/>
<propertyname="age"value="23"/>
</bean>
<beanid="chainedTestBean"class="org.springframework.beans.TestBean">
<test:debug/>
<test:nop/>
<propertyname="name"value="Rob Harrop"/>
<propertyname="age"value="23"/>
</bean>
<beanid="decorateWithAttribute"class="org.springframework.beans.TestBean"test:object-name="foo"/>
</beans>
相关的测试方法:
publicvoid testSimpleParser() throws Exception {
TestBean bean = (TestBean) this.beanFactory.getBean("testBean");
assetTestBean(bean);
}
publicvoid testSimpleDecorator() throws Exception {
TestBean bean = (TestBean) this.beanFactory.getBean("customisedTestBean");
assetTestBean(bean);
}
publicvoid testProxyingDecorator() throws Exception {
ITestBean bean = (ITestBean) this.beanFactory.getBean("debuggingTestBean");
assetTestBean(bean);
assertTrue(AopUtils.isAopProxy(bean));
Advisor[] advisors = ((Advised) bean).getAdvisors();
assertEquals("Incorrect number of advisors", 1, advisors.length);
assertEquals("Incorrect advice class.", DebugInterceptor.class, advisors[0].getAdvice().getClass());
}
publicvoid testChainedDecorators() throws Exception {
ITestBean bean = (ITestBean) this.beanFactory.getBean("chainedTestBean");
assetTestBean(bean);
assertTrue(AopUtils.isAopProxy(bean));
Advisor[] advisors = ((Advised) bean).getAdvisors();
assertEquals("Incorrect number of advisors", 2, advisors.length);
assertEquals("Incorrect advice class.", DebugInterceptor.class, advisors[0].getAdvice().getClass());
assertEquals("Incorrect advice class.", NopInterceptor.class, advisors[1].getAdvice().getClass());
}
publicvoid testDecorationViaAttribute() throws Exception {
RootBeanDefinition beanDefinition
= (RootBeanDefinition)this.beanFactory.getBeanDefinition("decorateWithAttribute");
assertEquals("foo", beanDefinition.getAttribute("objectName"));
}
privatevoid assetTestBean(ITestBean bean) {
assertEquals("Invalid name", "Rob Harrop", bean.getName());
assertEquals("Invalid age", 23, bean.getAge());
}