简单说明
Spring自定义标签是一个很常见的功能,常见的aop、mvc、tx等标签就是使用自定义标签的形式扩展的。 所以,如果我们需要自己实现一个类似于mvc驱动标签的功能的时候,封装为这种形式可以通过一行xml配置就开始使用,会变得非常方便。
开始
1、准备配置
首先新建文件 src/main/resources/META-INF/spring.handlers 和 src/main/resources/META-INF/spring.schemas
这两个文件内容如下
# spring.handlers
# 这个文件里的key就是spring的xml配置里的 xmlns:sample="http://www.dongpo.li/schema/sample"
http\://www.dongpo.li/schema/sample=li.dongpo.tc.service.SampleNamespaceHandler
# spring.schemas
# 这个k文件的key就是 xsi:schemaLocation 中的一部分
http\://www.dongpo.li/schema/sample-1.0.0.xsd=/META-INF/sample-1.0.0.xsd
http\://www.dongpo.li/schema/sample.xsd=/META-INF/sample-1.0.0.xsd
2、准备xsd文件
新建文件 src/main/resources/META-INF/sample-1.0.0.xsd
文件内容如下
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.dongpo.li/schema/sample"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://www.dongpo.li/schema/sample"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<!-- 这个name指定的sample就是<sample:sample /> 配置中的冒号后边的这个sample -->
<xsd:element name="sample">
<xsd:annotation>
<xsd:documentation>自定义标签扩展的示例</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<!-- 继承定义 从namespace="http://www.springframework.org/schema/beans" -->
<xsd:extension base="beans:identifiedType">
<!-- 定义了两个属性 name和value,name必填 -->
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="value" type="xsd:string" use="optional" default="value" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
3、配置
spring的xml文件配置示例如下
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:sample="http://www.dongpo.li/schema/sample"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.dongpo.li/schema/sample http://www.dongpo.li/schema/sample.xsd">
<sample:sample name="name" />
<bean id="sampleService" class="li.dongpo.tc.service.SampleServiceImpl" />
</beans>
其中重要的几个是:
1、xmlns:sample=“http://www.dongpo.li/schema/sample"
2、http://www.dongpo.li/schema/sample http://www.dongpo.li/schema/sample.xsd
3、<sample:sample name=“name” />
sampleService不是我们这次相关的配置,相关类请自行创建,主要是用来测试的和debug,可以按照自己的方式修改或删除。
4、主逻辑
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
public class SampleNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("sample", new BeanDefinitionParser() {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
String name = element.getAttribute("name");
String value = element.getAttribute("value");
System.out.println("BeanDefinitionParser start, name=" + name + ", value=" + value);
return null;
}
});
}
}
这样,通过我们刚新建的spring.handlers文件中的内容就能找到SampleNamespaceHandler这个类。使用的是spring自己实现的SPI机制,感兴趣的可以自行了解。
5、测试代码
public class Application {
public static void main(String[] args) throws InterruptedException {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
System.out.println("context初始化完成,开始获取bean");
}
}
执行这个代码就可以看到输出
BeanDefinitionParser start, name=name, value=value
教程就算结束了
使用场景
说这么热闹,这个功能到底有什么用
1、这个功能本身返回的就是BeanDefinition,所以是可以修改bean本身初始化方式的,参见aop的相关部分
2、可以在SampleNamespaceHandler开一个TCP端口做RPC调用,可以参见dubbo的相关实现
3、可以注入一个bean,其中建立TCP连接做消息监听。监听到特定消息反射执行对应的方法,可以实现诸如 mq、配置热加载等的功能
代码
我把代码上传到了github上,感兴趣的同学可以自行下载参照。 https://github.com/remainer-com/spring-sample 参见 spring-ns-sample 模块
后记
1、整体来说还是比较简单的,只是一个简单的Demo。
2、spring.schemas中还可以指定 http://www.dongpo.li/schema/sample.xsd=com/xxx/…/sample-1.0.0.xsd 将xsd文件放在和类同名的包里,具体可以参照aop的实现。