Spring 框架从 2.0 版本开始,提供了基于 Schema 风格的 XML 扩展机制,允许开发者扩展最基本的 Spring 配置文件(一般是 classpath 下的 spring.xml)。
试想一下,如果我们直接在 spring.xml 中加入一个自定义标签
现在我们来看下怎么实现这个功能,可以参考 Spring 帮助文档中的 extensible-xml.html。我们知道如果在需要在 spring.xml 中配置数据源,需要进行如下的配置:
1
2
3
4
5
6
| <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="username" value="root" /> <property name="password" value="1234" /> </bean> |
1
2
| <aty:datasource id="myDataSourcce" |
1. 提供一个 xsd 文件,负责对 xml 的标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| <?xml version="1.0" encoding="UTF-8"?> elementFormDefault="qualified" attributeFormDefault="unqualified"> <xsd:element name="datasource"> <xsd:complexType> <xsd:complexContent> <xsd:extension base="beans:identifiedType"> <xsd:attribute name="url" type="xsd:string" use="required" /> <xsd:attribute name="userName" type="xsd:string" use="required" /> <xsd:attribute name="password" type="xsd:string" use="required" /> </xsd:extension> </xsd:complexContent> </xsd:complexType> </xsd:element></xsd:schema> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| package net.aty.custom.define;import net.aty.custom.cfg.DataSourceInfo;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.beans.factory.config.BeanDefinitionHolder;import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;import org.springframework.beans.factory.support.RootBeanDefinition;import org.springframework.beans.factory.xml.BeanDefinitionParser;import org.springframework.beans.factory.xml.ParserContext;import org.w3c.dom.Element;public class DatasourceBeanDefinitionParser implements BeanDefinitionParser { public BeanDefinition parse(Element element, ParserContext context) { RootBeanDefinition def = new RootBeanDefinition(); // 设置Bean Class def.setBeanClass(DataSourceInfo.class); // 注册ID属性 String id = element.getAttribute("id"); BeanDefinitionHolder idHolder = new BeanDefinitionHolder(def, id); BeanDefinitionReaderUtils.registerBeanDefinition(idHolder, context.getRegistry()); // 注册属性 String url = element.getAttribute("url"); String userName = element.getAttribute("userName"); String password = element.getAttribute("password"); BeanDefinitionHolder urlHolder = new BeanDefinitionHolder(def, url); BeanDefinitionHolder userNameHolder = new BeanDefinitionHolder(def, userName); BeanDefinitionHolder passwordHolder = new BeanDefinitionHolder(def, password); BeanDefinitionReaderUtils.registerBeanDefinition(urlHolder, context.getRegistry()); BeanDefinitionReaderUtils.registerBeanDefinition(userNameHolder, context.getRegistry()); BeanDefinitionReaderUtils.registerBeanDefinition(passwordHolder, context.getRegistry()); def.getPropertyValues().addPropertyValue("url", url); def.getPropertyValues().addPropertyValue("userName", userName); def.getPropertyValues().addPropertyValue("password", password); return def; }} |
3. 定义个 NamespaceHandler,由 Sping 框架的调用入口。这也是我们自定义 xml 解析的入口
1
2
3
4
5
6
7
8
9
10
| package net.aty.custom.define;import org.springframework.beans.factory.xml.NamespaceHandlerSupport;public class DatasourceNamespaceHandlerSupport extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("datasource", new DatasourceBeanDefinitionParser()); }} |
Spring 没那么聪明,它无法知道我们在什么地方定义了哪些扩展标签,这些标签将被谁解析,怎么解析。这个过程要我们通过一些配置文件来告知 Spring 知道,它们就是 spring.handlers 和 spring.schemas,它们放在 META-INF 目录中。Spring.jar 的 META-INF 目录中也有同名的文件,它们的文件内容基本上是相似的,而 Spring 在执行过程中,如果发现其他 jar 文件的 META-INF 文件夹中包含有这两个文件,Spring 将会合并它们。
spring.handlers 内容如下:
1
| http\://www.aty.com/schema/aty=net.aty.custom.define.DatasourceNamespaceHandlerSupport |
1
| http\://www.aty.com/schema/aty.xsd=aty.xsd |
测试工程的 spring.xml 配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
| <?xml version="1.0" encoding="UTF-8"?> xsi:schemaLocation="http://www.springframework.org/schema/beans <aty:datasource id="myDataSourcce" </beans> |
1
2
3
4
5
6
7
8
9
10
11
12
13
| import net.aty.custom.cfg.DataSourceInfo;import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestMain { private static ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); public static void main(String[] args) { DataSourceInfo d = (DataSourceInfo) context.getBean("myDataSourcce"); System.out.println(d); }} |
转载请并标注: “本文转载自 linkedkeeper.com (文/张松然)”