由于以前的帐号,手机绑定有问题,无法操作了,在此新开了一个博客。
这次我们一起分析下,自定义标签的解析。
在完成从配置文件到Document的转换并提取对应的root后,将开始了所有元素的解析,而在这个过程中便开始了默认标签与自定义标签,两种格式的区分。函数如下:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if(delegate.isDefaultNamespace(root)){
NodeList nl = root.getChildNodes();
for(int i = 0;i<nl.getLength();i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element)node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}else{
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
在文章中,所有的功能都围绕一句代码
delegate.parseCustomElement(root);开展的。从上面的函数我们可以看出,当Spring拿到一个元素时首先要做的是根据命名空间进行解析,如果是默认的命名空间,
则使用parseDefaultElement方法进行元素解析,否则使用parseCustomElement方法进行解析。在分析自定义标签的解析过程之前,我们先了解一下自定义标签的使用过程。
1.自定义标签的使用
在很多情况下,我们需要为系统提供可配置化支持,简单的做法可以直接基于Springde 标准bean来配置,但配置较为复杂或者需要更多丰富控制的时候,会显得非常笨拙。一般的做法会用原生态的方式去解析定义好的XML文件,然后转化为配置对象。这种方式当然可以解决所有问题,但实现起来比较繁琐,特别是配置非常复杂的时候,解析工作是一个不得不考虑的负担。Spring提供了可扩展的Schema支持,这一个不错的折中方案,扩展Spring自定义标签配置大致需要一下几个步骤(前提是要把Spring的Core包加入项目中)。
- 创建一个需要扩展的组件
- 定义一个XSD文件描述组件内容
- 创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义
- 创建一个Handler文件,扩展自NamespaceHandlerSupport,目的是将组件注册到Spring容器。
- 编写Spring.handlers和spring.schmas文件
现在我们就按照上面的步骤带领读者一步步地体验自定义标签的过程。
(1)首先我们创建一个普通的POJO,这个POJO没有任何特别之处,只是用来接收配置文件。
public class User {
private String userName;
private String email;
}
(2)定义一个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="userName" type="string">
<attribute>name="email" type="string">
</complexType>
</element>
</schema>
在上面的XSD文件中描述了一个新的targetNamespace,并在这个空间中定义了一个name为user的element,uer有3个属性id,userName和email,其中email类型为string。这个三个类主要用于验证Spirng配置文件自定义格式。
(3)创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义。
//从element中解析并提取对应的元素
protected void doParse(Element element, BeanDefinitionBuilder bean) {
String userName = element.getAttributes("userName");
String email = element.getAttributes("email");
//将提取的数据放入到BeanDefinitionBuilder中,待到完成所有bean的解析后统一注册到beanFactory中
if(StringUtils.hasText(userName)){
bean.addPropertyValue("userName", userName);
}
if(StringUtils.hasTest(email)){
bean.addPropertyValue("email", email);
}
}
创建一个Handler文件,扩展自NamespaceHandlerSupport,目的是将组件注册到Spirng容器。
public class MyNamespaceHandler extends NamespaceHandlerSupport{
public void init() {
registerBeanDefinitionParser("user",new UserBeanDefinitionParser());
}
}
以上代码很简单,无非是当遇到自定义标签<user:aaa 这样的类似于user开头的元素,就会把这个元素扔给对应的UserBeanDefinitionParser去解析。(5)编写Spirng.handlers和Spring.schemas文件,默认位置是在工程的/META-INF/文件夹下。
- Spring.handlers
http\://www.lexueba.com/schema/user=test.customtag.MyNamespaceHandler
- Spring.schemas
http\://www.lexueba.com/Schema、user.xsd=META-INF/Spring-test.xsd
到这里,自定义的配置就结束了,而Spring加载自定义的大致流程是遇到自定义标签然后就去Spring.handlers和Spring.schemas中去找对应的handler和XSD,默认位置是/META-INF/下,进而有找到对应handler以及解析元素的Parser,从而完成了整个自定义元素的解析,也就是说自定义与Spring中默认的标准配置不同在与Spring将自定义标签解析的工作委托给了用户去实现。
(6)创建测试配置文件,在配置文件中引入对应的命名空间以及XSD后,便可以直接使用自定义标签了。
<beans xmlns="http://www.Springframeword.org/schema/beans"
xmlns:xsi="http://www.lexueba.com/schema/user"
xmlns:myname="http://www.lexueba.com/schema/user"
xsi:schemaLocation="http://www.Springframework.org/schema/beans http://www.Springframework.org/schema/beans/Spring-beans-2.0.xsd
http://www.lexueba.com/schema/user http://www.lexueba.com/schema/user.xsd>
<user:user id="testbean" userName="aaa" email="bbb"/>
</beans>