介绍
Spring框架从2.0版本开始,提供了基于Schema风格的XML扩展机制,允许开发者扩展最基本的spring配置文件,这样我们就可以编写自定义的xml bean解析器然后集成到Spring IoC容器中。很多支持Spring的框架,比如Dubbo、mybatis都提供了对Spring xml扩展的支持。
Xml扩展大概有以下几个步骤:
- 编写xml schema来描述自定义元素
- 编写自定义类
- 编写
NamespaceHandler
的实现类 - 编写
BeanDefinitionParser
实现类 - 把上述组件注册到Spring
编写自定义schema
1 | <?xml version="1.0" encoding="UTF-8"?> |
编写自定义类
1 | public class Robot { |
然后我们就可以在xml中定义如下的robot元素:1
<product:robot id="bb8" brand="StarWar" type="bb8" height="100" weight="20"/>
编写NamespaceHandler
NamespaceHandler
用于解析我们自定义名字空间下的所有元素,目前我们要解析上面的product:robot
元素。NamespaceHandler
里面只有3个方法:
init()
会在NamespaceHandler
初始化的时候被调用。BeanDefinition parse(Element, ParserContext)
- 当Spring遇到一个顶层元素的时候被调用。BeanDefinitionHolder decorate(Node, BeanDefinitionHolder, ParserContext)
- 当Spring遇到一个属性或嵌套元素的时候调用.
Spring提供了默认实现类NamespaceHandlerSupport
,我们只需在init的时候注册每个元素的解析器即可。
1 | public class ProductNamespaceHandler extends NamespaceHandlerSupport { |
编写BeanDefinitionParser
这样在解析xml过程中遇到robot元素时,Spring会交给RobotBeanDefinitionParser来解析。RobotBeanDefinitionParser取出相应的属性然后设置到bean中。
1 | public class RobotBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { |
注册handler和schema
为了让Spring在解析xml的时候能够感知到我们的自定义元素,我们需要把namespaceHandler
和xsd文件放到2个指定的配置文件中,这2个文件都位于META-INF
目录中。
META-INF/spring.handlers
`spring.handlers`
文件包含了xml schema uri和handler类的映射关系,比如:
http\://www.mycompany.com/schema/product=spring.xml.ext.schema.ProductNamespaceHandler
这表示遇到http://www.mycompany.com/schema/product
命名空间的时候会交给ProductNamespaceHandler
来处理。
注意上面的冒号转义。
key部分必须和xsd文件中的targetNamespace
值保持一致。
META-INF/spring.schemas
http\://www.mycompany.com/schema/product.xsd=META-INF/product.xsd
最后测试下
写个Spring配置文件products.xml:1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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.mycompany.com/schema/product"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.mycompany.com/schema/product
http://www.mycompany.com/schema/product.xsd">
<product:robot id="bb8" brand="StarWar" type="bb8" height="100" weight="20"/>
</beans>
测试类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 (SpringJUnit4ClassRunner.class)
"classpath:products.xml" }) (locations = {
public class SchemaTest {
"bb8") (
private Robot robot;
public void propertyTest() {
assertNotNull(robot);
String brand = robot.getBrand();
String type = robot.getType();
int height = robot.getHeight();
float weight = robot.getWeight();
assertEquals("Brand should be bb8.", "bb8", brand);
assertEquals("Type should be bb8.", "bb8", type);
assertEquals("Height should be 100.", 100, height);
assertEquals("Weight should be 20.", 20, weight);
}
}