Spring 扩展自定义标签案例实现
在很多情况下,我们需要为系统提供可配置化支持,简单的做法可以直接基于Spring的标准Bean来配置,但配置较为复杂或者需要更多丰富控制的 时候,会显得非常笨拙。一般的做法会用原生态的方式去解析定义好的xml文件,然后转化为配置对象,这种方式当然可以解决所有问题,但实现起来比较繁琐, 特别是是在配置非常复杂的时候,解析工作是一个不得不考虑的负担。Spring提供了可扩展Schema的支持,这是一个不错的折中方案。
扩展Spring自定义标签大致需要如下几步:
1.创建需要扩展的组件(设计配置属性和JavaBean)
2.定义XSD文件描述组件内容(自定义的合法构建模块),主要用于定义数据约束;
3.创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义
4.创建Handler文件,扩展字NamespaceHandlerSupport,目的是将组件注册到Spring容器
5.编写Spring.handlers和Spring.schemas文件
6.搞定可以在应用中使用了
设计配置属性和JavaBean
首先当然得设计好配置项,并通过JavaBean来建模,本例中需要配置People实体,配置属性name和age(id是默认需要的)
public class MyBeanJavaBean {
//默认需要
private String id;
private String name;
private String url;
...省略set get方法...
}
编写XSD文件
为上一步设计好的配置项编写XSD文件,XSD是schema的定义文件,配置的输入和解析输出都是以XSD为契约,本例中XSD如下:src/main/resources/META-INF/MyBeanJavaBean.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns="http://www.example.org/schema/MyBeanJavaBean"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://www.example.org/schema/MyBeanJavaBean"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:element name="MyBeanJavaBean">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="url" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
描述了一个targetNamespace,并且创建了一个element MyBeanJavaBean。里面有三个attribute。主要是为了验证Spring配置文件中的自定义格式。再进一步解释,就是,Spring位置文件中使用的MyBeanJavaBean自定义标签中,属性只能是上面的三种,有其他的属性的话,就会报错。
关于xsd:schema的各个属性具体含义就不作过多解释,可以参见http://www.w3school.com.cn/schema/schema_schema.asp
<xsd:element name="people">对应着配置项节点的名称,因此在应用中会用people作为节点名来引用这个配置
<xsd:attribute name="name" type="xsd:string" />对应着属性名和类型
完成后需把xsd存放在classpath下,一般都放在META-INF目录下(本例就放在这个目录下)
编写NamespaceHandler和BeanDefinitionParser完成解析工作
下面需要完成解析工作,会用到NamespaceHandler和BeanDefinitionParser这两个概念。具体说来 NamespaceHandler会根据schema和节点名找到某个BeanDefinitionParser,然后由 BeanDefinitionParser完成具体的解析工作。因此需要分别完成NamespaceHandler和 BeanDefinitionParser的实现类,Spring提供了默认实现类NamespaceHandlerSupport和 AbstractSingleBeanDefinitionParser,简单的方式就是去继承这两个类。
创建文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义
package org.alexsotob.spring.extension;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
public class MyBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@SuppressWarnings("rawtypes")
protected Class getBeanClass(Element element) {
return MyBeanJavaBean.class;
}
/**
* 解析节点数据
*/
protected void doParse(Element element, BeanDefinitionBuilder bean) {
String userName = element.getAttribute("name");
String email = element.getAttribute("url");
if (StringUtils.hasText(userName)) {
bean.addPropertyValue("userName", userName);
}
if (StringUtils.hasText(email)){
bean.addPropertyValue("email", email);
}
}
}
上文,虽说是继承了AbstractSingleBeanDefinitionParser ,但根本上来说,是实现了BeanDefinitionParser接口。
创建扩展NamespaceHandler
扩展自NamespaceHandlerSupport,目的是将组件注册到Spring容器中。
package org.alexsotob.spring.extension;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class MyBeanNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("MyBean", new MyBeanDefinitionParser());
}
}
编写Spring.handlers和Spring.schemas文件
上面几个步骤走下来会发现开发好的handler与xsd还没法让应用感知到,就这样放上去是没法把前面做的工作纳入体系中的,spring提供了 spring.handlers和spring.schemas这两个配置文件来完成这项工作,这两个文件需要我们自己编写并放入META-INF文件夹 中,这两个文件的地址必须是META-INF/spring.handlers和META-INF/spring.schemas,spring会默认去 载入它们
路径:
src/main/resources/META-INF/spring.handlers
src/main/resources/META-INF/spring.schemas
根据对应的命名空间找到对应的xsd文件以及handler类
创建测试配置文件
路径:src/main/resources/test.xml
<?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:myBean="http://www.example.org/schema/MyBeanJavaBean"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.example.org/schema/MyBeanJavaBean http://www.example.org/schema/MyBeanJavaBean.xsd">
<myBean:MyBean id = "firstMyBean" name = "lee" url = "www.baidu.com"/>
</beans>
在第4行这里引入了myBean对应的命名空间,它会到第六行(http://www.example.org/schema/MyBeanJavaBean.xsd)找到对应XSD文件进行check。第六行的http://www.example.org/schema/MyBeanJavaBean.xsd 找到对应的文件的位置,在前面spring.schemas可以找到
编写测试
package org.alexsotob.spring.extension;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext beans=new ClassPathXmlApplicationContext("classpath:test.xml");
MyBeanJavaBean myBean=(MyBeanJavaBean)beans.getBean("firstMyBean");
System.out.println(myBean);
}
}
最后效果
本此主要讲解的是如何自定实现标签解析,那么思考????,spring是如何扩展能让我们自定标签,并且能和本身的程序代码进行融合呢?后面会继续讲解,一起努力