spring我们在开发过程中是个必不可少的框架,我们通常会将我们程序中的bean交由spring容器来进行管理,应用程序需要用到bean的时候从spring容器中去获取,spring是如何实现这种方式的呢?spring的配置文件,当我们在开发的过程中发现spring提供的配置命令不满足的情况下我们该怎么办呢?我们是不是要自己去解析原生的xml文件?当然不用spring为开发者提供了可扩展的schema的支持,可以支持自定义配置。
0x01 扩展配置步骤
在classpath的META-INF下定义另个文件:spring.handlers、spring.schemas(识别配置)
自定义xsd文件(定义配置)
继承org.springframework.beans.factory.xml.NamespaceHandlerSupport、org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser(解析配置)
0x02 识别配置
spring如何识别自定的schema文件
spring.handlers指明命名空间需要哪个类来处理。
spring.schemas指明了schema文件的位置,spring会使用这里制定的xsd文件来验证配置的正确性。
spring容器在启动的时候会根据这两个文件的配置加载文件内容,然后去解析文件中的配置信息。下面来看下具体的配置信息:
spring.handlers文件的配置内容:
http\://code.liutxer.com/schema/bymq=com.liutxer.by.conf.RpcNamespaceHandler
http://code.liutxer.com/schem…这个命名空间使用com.liutxer.by.conf.RpcNamespaceHandler这个类来处理。
spring.schemas文件的配置内容:
http\://code.liutxer.com/schema/bymq/rpc.xsd=META-INF/rpc.xsd
http://code.liutxer.com/schem…文件的位置在classpath:/META-INF/rpc.xsd中
0x03 定义配置
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns="http://code.liutxer.com/schema/bymq"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://code.liutxer.com/schema/bymq"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:element name="service">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="interface" type="xsd:string" />
<xsd:attribute name="ref" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="reference">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="z" type="xsd:string" />
<xsd:attribute name="interface" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
其中要注意的点主要有xmlns–xml namespace、targetNamespace这两个的定义。
0x04 解析配置
通过实现NamespaceHandlerSupport、BeanDefinitionParser完成对自定义的schema文件的解析工作。
NamespaceHandlerSupport会根据schema的节点名称来找到某个BeanDefinitionParser然后由BeanDefinitionParser来完成具体的解析工作,因此我们需要具体实现自己的NamespaceHandlerSupport和BeanDefinitionParser。下面看下具体实现:
public class RpcNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("service", new RpcBeanDefinitionParser(ServerBean.class));
registerBeanDefinitionParser("reference", new RpcBeanDefinitionParser(ReferenceBean.class));
}
}
上面实例主要说明service这个自定义标签通过new RpcBeanDefinitionParser(ServerBean.class)通过这个parser进行解析。
public class RpcBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
private Class<?> beanClass;
public RpcBeanDefinitionParser(Class<?> beanClass) {
this.beanClass = beanClass;
}
protected Class getBeanClass(Element element) {
return this.beanClass;
}
protected void doParse(Element element, BeanDefinitionBuilder bean) {
if (ServerBean.class.equals(this.beanClass)) {
String interfaceVal = element.getAttribute("interface");
String refVal = element.getAttribute("ref");
Object reference = new RuntimeBeanReference(refVal);
if (StringUtils.hasText(interfaceVal)) {
bean.addPropertyValue("interfaceVal", interfaceVal);
}
if (reference != null) {
bean.addPropertyValue("ref", reference);
}
} else if (ReferenceBean.class.equals(this.beanClass)) {
String beanName = element.getAttribute("id");
String interfaceVal = element.getAttribute("interface");
if (StringUtils.hasText(interfaceVal)) {
bean.addPropertyValue("interfaceVal", interfaceVal);
}
if (StringUtils.hasText(beanName)) {
bean.addPropertyValue("beanName", beanName);
}
}
}
}
具体的BeanDefinitionParser实现解析操作,上面的实现比较简单就是将对应标签的属性值取出来然后构建出具体的bean。
到这整个扩展schema该进行的操作就都已经就绪了。
0x05 使用配置
spring配置文件:
<?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:liutxer="http://code.liutxer.com/schema/bymq"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.liutxer.com/schema/bymq http://code.liutxer.com/schema/bymq/rpc.xsd ">
<bean id="kkk" class="com.liutxer.bymq.conf.DemoTest"></bean>
<liutxer:service id="jjj" interface="com.liutxer.bymq.conf.IDemoTest" ref="kkk"/>
<liutxer:reference id="demoTest" interface="com.liutxer.bymq.conf.IDemoTest"></liutxer:reference>
</beans>
这里通过spring的配置对扩展的schema定义配置,当spring容器加载的时候会加载、解析这份自定义的配置,实现扩展。
实例:
加载配置获得自定义的配置对象进行调用。
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:app.xml");
IDemoTest demoTest = (IDemoTest) ctx.getBean("demoTest");
System.out.println("spring rpc test:" + demoTest.get());
}
}