基于Spring的Schema扩展
阅读Spring源码中,发现Spring提供了可扩展Schema的支持,让我想到了其他的框架中的自定义标签是否也是使用了这个。比如,dubbo中的一些可扩展Schema的支持。在翻阅源码后,发现dubbo也是使用了Spring提供的Schema的支持。根据Spring的介绍,分为如下几个步骤:
- 设计配置写对应的Bean
- 编写XSD文件
- 编写NamespaceHandler和BeanDefinitionParser完成解析工作
- 编写spring.handlers和spring.schemas串联起所有部件
下面介绍一个例子来带入
首先我们要想要设计一个Sasukeer的扩展比如是
<myName:sasukeer blogUrl="www.baidu.com" name="Sasukeer" age="23"/>
那么首先需要一个对应的javaBean如下:
public class Sasukeer {
private String blogUrl;
private String name;
private String age;
}
下一步需要编写xsd文件来,来最为一个编写和解析的规则:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns="http://github.com/wkztselina/sasukeer"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
elementFormDefault="qualified"
targetNamespace="http://github.com/wkztselina/sasukeer"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:element name="sasukeer">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="age" type="xsd:string" />
<xsd:attribute name="blogUrl" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
一般把XSD文件,放在META-INF的目录下面,这个目录一般都是放置用来配置应用程序、扩展程序、类加载器和服务的文件。就好像dubbo中使用了java的spi机制,就把具体实现类的信息放在改目录下面,方便为某个接口寻找实现类,也方便修改实现类。同样,关于xsd:schema具体含义就不作过多解释,参见http://www.w3school.com.cn/schema/schema_schema.asp
下一步编写对应的解析类
public class MyBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return Sasukeer.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String name = element.getAttribute("name");
String age = element.getAttribute("age");
String blogUrl = element.getAttribute("blogUrl");
if (StringUtils.hasText(blogUrl)) {
builder.addPropertyValue("blogUrl", blogUrl);
}
if (StringUtils.hasText(name)) {
builder.addPropertyValue("name", name);
}
if (StringUtils.hasText(age)) {
builder.addPropertyValue("age", age);
}
}
}
public class MyNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("sasukeer",new MyBeanDefinitionParser());
}
}
通过继承NamespaceHandlerSupport,可以让我们指定sasukeer的解析器。然后由我们指定的解析器来解析,同时解析器继承AbstractSingleBeanDefinitionParser可以简化操作一部分解析的操作。
当完成上面的操作之后,我们需要的组件已经完成,只需要把他们之间关联起来就可以了。所以,需要编写spring.handlers和spring.schemas串联起所有部件。本例中的spring.handlers如下:
http\://github.com/wkztselina/sasukeer= wkzt.test.xsd.MyNamespaceHandler
所以当使用到名为"http://github.com/wkztselina/sasukeer"的引用的时候,就会调用MyNamespaceHandler来进行解析
spring.schemas如下
http\://github.com/wkztselina/sasukeer.xsd=META-INF/sasukeer.xsd
spring.schemas和spring.handlers都需要在META-INF目录下面
测试一下:如下
先写配置文件:
<?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:myName="http://github.com/wkztselina/sasukeer"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://github.com/wkztselina/sasukeer http://github.com/wkztselina/sasukeer.xsd">
<myName:sasukeer id="test" blogUrl="www.baidu.com" name="Sasukeer" age="23"/>
</beans>
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
Sasukeer p = (Sasukeer)ctx.getBean("test");
System.out.println(p.getBlogUrl());
System.out.println(p.getName());
System.out.println(p.getAge());
}
就可以加载到想要的结果。
www.baidu.com
Sasukeer
23
在mvn的项目中,可以再resources中创建META-INF目录,网上有遇到过mvn项目构建时覆盖了自己的META-INF目录,可以用如下代码:
<build>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<classesDirectory>target/classes/</classesDirectory>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
</plugin>
</plugins>
</build>
在了解Spring的扩展以后,查看一下dubbo的源码,是否也是这样在使用如下:
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
@Override
public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}
}
可以看到service、reference、consumer等都在。由于dubbo所支持的标签不多,所以都对其做了DubboBeanDefinitionParser的解析。由此可以得到,dubbo也是使用了Spring的扩展。