Dubbo学习-dubbo自定义配置标签
使用过dubbo的朋友都知道,dubbo有很多自定义的配置标签,比如<dubbo:service />、<dubbo:reference />等。那么这些自定义是怎么实现的呢? dubbo是运行在spring容器中,dubbo的配置文件也是通过spring的配置文件applicationContext.xml来加载,
使用过dubbo的朋友都知道,dubbo有很多自定义的配置标签,比如<dubbo:service />、<dubbo:reference />等。那么这些自定义是怎么实现的呢?
dubbo是运行在spring容器中,dubbo的配置文件也是通过spring的配置文件applicationContext.xml来加载,所以dubbo的自定义配置标签实现,其实是依赖spring的xml schema机制,下面来看一下dubbo的实现过程。
上图是dubbo实现自定义配置标签的源码工程(git地址:https://github.com/apache/incubator-dubbo/tree/master/dubbo-config/dubbo-config-spring),核心的那几个文件:
- dubbo.xsd(dubbo的自定义schema标签文件,里面定义了dubbo所有标签属性)
- spring.schemas(配置spirng自定义schema标签文件位置)
- spring.handlers(配置spirng自定义schema标签处理器类)
- DubboNamespaceHandler.java(spirng自定义schema标签处理器类)
- DubboBeanDefinitionParser.java(spirng自定义schema标签解析类)
-----------------------------------------------------------------------------------------------------------------------------
接下来我参考了dubbo源码,自己实现了一个简单的自定义配置标签项目,希望能帮到大家。先上代码结构图(源码地址在:https://gitee.com/plg17/plg-dubbo/tree/master/dubbo_common/CustomNamespace):
1、自定义配置标签属性bean类(ConsumerConfig.java)
ConsumerConfig.java是一个不同的java bean类,其中定义了本次自定义标签中所需要的属性,就像dubbo中的配置<dubbo:service />,有interface、ref、version等等配置项。自定义schema标签配置文件中的属性要跟ConsumerConfig.java中定义的属性一直,否则会报错。
/**
* 自定义schema属性bean
*
* @author Administrator
*
*/
public class ConsumerConfig implements Serializable {
private static final long serialVersionUID = -5368563657151220211L;
// id
protected String id;
// timeout for remote invocation in milliseconds
protected Integer timeout;
// retry times
protected Integer retries;
// max concurrent invocations
protected Integer actives;
2、自定义schema标签文件(myCustom.xsd)
自定义shema标签文件,名字可以随便取,后缀为.xsd(xsd文件规范可以参考http://blog.sina.com.cn/s/blog_ad0672d60102uy6w.html)。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:tool="http://www.springframework.org/schema/tool"
xmlns="http://gitee.com/plg17/plg-dubbo/myCustom" targetNamespace="http://gitee.com/plg17/plg-dubbo/myCustom">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:import namespace="http://www.springframework.org/schema/tool" />
<xsd:complexType name="consumerType">
<xsd:attribute name="id" type="xsd:string" use="optional" default="">
<xsd:annotation>
<xsd:documentation>
<![CDATA[ The exclusive id. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="timeout" type="xsd:string" use="optional" default="0">
<xsd:annotation>
<xsd:documentation><![CDATA[ The method invoke timeout. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="retries" type="xsd:string" use="optional" default="1">
<xsd:annotation>
<xsd:documentation><![CDATA[ The method retry times. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="actives" type="xsd:string" use="optional" default="100">
<xsd:annotation>
<xsd:documentation><![CDATA[ The max active requests. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:element name="consumer" type="consumerType">
<xsd:annotation>
<xsd:documentation><![CDATA[ Export service default config ]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:schema>
注意红色标注的地方:
1. xmlns="http://gitee.com/plg17/plg-dubbo/myCustom" >:xml命名空间,普遍都命名成url的形式,但终究不是url,不要求能访问。这个名字空间会在spring的applicationContext.xml配置文件中用到。
2. "id"、"timeout"、"retries"、"actives":这里的属性一定是ConsumerConfig.java中的属性的子集,如果xsd文件中的属性不存在与ConsumerConfig.java,那启动的时候回报错。
3. <xsd:element name="consumer" type="consumerType">:这里的consumerType和<xsd:complexType name="consumerType">一样,是一种映射关系的配置。而consumer则是自定义标签的元素名称,跟MyNamespaceHandler.java中配置的元素名称一致。
自定义schema标签的处理器,继承于org.springframework.beans.factory.xml.NamespaceHandlerSupport。用于处理自定义标签的命名空间。
package com.plg.dubbo.customschema;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* 自定义schema标签的处理器
*
* @author Administrator
*
*/
public class MyNamespaceHandler extends NamespaceHandlerSupport {
/**
* 注册自定义标签的命名空间
*/
@Override
public void init() {
// 格式:registerBeanDefinitionParser(自定义标签的命名空间, spring容器的bean对象实例)
// 这里的自定义标签元素名称:"consumer",跟applicationContext.xml中的配置<myCustom:consumer />一致
registerBeanDefinitionParser("consumer", new MyCustomBeanDefinitionParser(ConsumerConfig.class, true));
}
}
4、spirng自定义schema标签解析类(MyCustomBeanDefinitionParser.java)
自定义标签解析类,实现org.springframework.beans.factory.xml.BeanDefinitionParser接口。实现将自定义的标签封装成spring内部的bean对象RootBeanDefinition,并注册到spring容器中。
package com.plg.dubbo.customschema;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* 自定义schema标签的解析类
*
* @author Administrator
*
*/
public class MyCustomBeanDefinitionParser implements BeanDefinitionParser {
private static final Logger logger = LoggerFactory.getLogger(MyCustomBeanDefinitionParser.class);
private final Class<?> beanClass;
private final boolean required;
public MyCustomBeanDefinitionParser(Class<?> beanClass, boolean required) {
this.beanClass = beanClass;
this.required = required;
}
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
return parse(element, parserContext, beanClass, required);
}
/**
* 解析自定义schema文件,并出注册到spring容器中
*
* @param element
* xml配置文件中的配置项
* @param parserContext
* spring上下文
* @param beanClass
* 自定义schema标签对应的java bean文件
* @param required
* 是否必须(dubbo中有些配置不是必须的)
* @return
*/
private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
String id = element.getAttribute("id");
String timeout = element.getAttribute("timeout");
String retries = element.getAttribute("retries");
String actives = element.getAttribute("actives");
if (!StringUtils.isEmpty(id)) {
// 重复spring bean校验
if (parserContext.getRegistry().containsBeanDefinition(id)) {
logger.warn("重复spring bean id,id={}", id);
throw new IllegalStateException("Duplicate spring bean id " + id);
}
} else {
logger.warn("spring bean id不能为null");
throw new IllegalStateException("spring bean id can not be null");
}
// 把bean封装成RootBeanDefinition对象,RootBeanDefinition继承了BeanDefinition
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);// 设置这个bean是否延迟初始化,false-启动时就初始化
beanDefinition.getPropertyValues().addPropertyValue("id", id);
if (!StringUtils.isEmpty(timeout)) {
beanDefinition.getPropertyValues().addPropertyValue("timeout", timeout);
}
if (!StringUtils.isEmpty(retries)) {
beanDefinition.getPropertyValues().addPropertyValue("retries", retries);
}
if (!StringUtils.isEmpty(actives)) {
beanDefinition.getPropertyValues().addPropertyValue("actives", actives);
}
// 把RootBeanDefinition bean对象注册到spring容器中
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
return beanDefinition;
}
}
5、spring.handlers和spring.schemas
spring.handlers和spring.schemas,扩展spring schema标签要求要有这两个配置文件,名字不可修改,spring默认会去加载。
spring.schemas:配置自定义schema标签文件的位置
http\://gitee.com/plg17/plg-dubbo/myCustom.xsd=META-INF/myCustom.xsd
spring.handlers:配置自定义标签的处理器类
http\://gitee.com/plg17/plg-dubbo/myCustom=com.plg.dubbo.customschema.MyNamespaceHandler
6、spring配置文件(applicationContext.xml
)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns="http://www.springframework.org/schema/beans"
xmlns:myCustom="http://gitee.com/plg17/plg-dubbo/myCustom"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://gitee.com/plg17/plg-dubbo/myCustom
http://gitee.com/plg17/plg-dubbo/myCustom.xsd">
<!--
<myCustom:consumer />
1.myCustom:跟本文件中的xmlns:myCustom一致
2.consumer:跟MyNamespaceHandler.java中定义的自定义标签元素名称要一致
-->
<myCustom:consumer id="consumer" timeout="12000" retries="1" actives="100" />
</beans>
注意红色标注部分:
1.url格式的配置就是spring.handlers和spring.schemas中配置的命名空间名称了。
2.id、timeout、retries、acrives就是ConsumerConfig.java中的属性。
3.<myCustom:consumer />,myCustom是配置文件中的命名空间的key,consumer跟MyNamespaceHandler.java中定义的自定义标签元素名称一致。
7、spring启动器和测试结果(Main.java)package com.plg.dubbo.customschema;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 启动类
*
* @author Administrator
*
*/
public final class Main {
private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
private Main() {
throw new IllegalAccessError("Utility class");
}
/**
* @param args
*/
@SuppressWarnings("resource")
public static void main(String[] args) {
try {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath*:/META-INF/applicationContext.xml");
ConsumerConfig config = (ConsumerConfig) context.getBean("consumer");
System.out.println("id = " + config.getId());
System.out.println("timeout = " + config.getTimeout());
System.out.println("actives = " + config.getActives());
System.out.println("retries = " + config.getRetries());
} catch (Exception e) {
LOGGER.error("运行异常", e);
}
}
}
启动spring容器,并输出自定义配置中的属性值,执行结果:
欧了!
8、注意
如果applicationContext.xml配置文件中的自定义标签配置中出现的属性不包含在ConsumerConfig.java,那就会报错,如把ConsumerConfig.java中的id属性删掉,启动spring容器时就报异常:
[org.springframework.context.support.ClassPathXmlApplicationContext] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@dcf3e99: startup date [Sat Apr 14 20:58:24 CST 2018]; root of context hierarchy
[org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from URL [file:/D:/JAVA/workspace(learing)/plg-dubbo/dubbo_common/CustomNamespace/target/classes/META-INF/applicationContext.xml]
[com.plg.dubbo.customschema.Main] - 运行异常
org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 13 in XML document from URL [file:/D:/JAVA/workspace(learing)/plg-dubbo/dubbo_common/CustomNamespace/target/classes/META-INF/applicationContext.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 13; columnNumber: 79; cvc-complex-type.3.2.2: 元素 'myCustom:consumer' 中不允许出现属性 'id'。
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:399)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:217)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:252)
at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:127)
at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:93)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:537)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:452)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at com.plg.dubbo.customschema.Main.main(Main.java:26)
Caused by: org.xml.sax.SAXParseException; lineNumber: 13; columnNumber: 79; cvc-complex-type.3.2.2: 元素 'myCustom:consumer' 中不允许出现属性 'id'。
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:134)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:396)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:284)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator$XSIErrorReporter.reportError(XMLSchemaValidator.java:452)
... 14 more
更多推荐
所有评论(0)