讀DUBBOX源碼之自定義基于Spring可擴展Schema的配置xml文件
DubboX是一個開源的分布式服務(wù)框架,是當(dāng)當(dāng)對阿里Dubbo的一個擴展甘苍。目前DubboX的最新版本為2.8.4
Dubbo有xml、屬性和悦、API赎瞎、注解共4中配置方式。DubboX的xml配置文件是基于Spring可擴展Schema來自定義的郎楼。通過Spring加載Dubbo配置,并且對應(yīng)用沒有任何侵入性窒悔,可以應(yīng)用于使用Spring框架的系統(tǒng)中呜袁。
一、provider.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:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:application name="hello-world-app" />
<dubbo:registry address="multicast://224.5.6.7:1234" />
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoServiceLocal" />
<dubbo:reference id="demoServiceRemote" interface="com.alibaba.dubbo.demo.DemoService" />
</beans>
所有標簽都支持自定義參數(shù)简珠,用于不同擴展點實現(xiàn)的特殊配置阶界,如:
<dubbo:protocol name="jms">
<dubbo:parameter key="queue" value="your_queue" />
</dubbo:protocol>
或:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:protocol name="jms" p:queue="your_queue" />
</beans>
dubbo:application、dubbo:registry等標簽都是利用Spring的XMl擴展機制來進行解析加載的聋庵。下面看看Spring如何解析這些標簽的膘融。
二、擴展標簽的解析
解析擴展的標簽祭玉,主要通過配置文件和解析bean來實現(xiàn)氧映。(Dubbox的xml配置是在/dubbo-config-spring模塊中)
1、配置文件
- dubbo.xsd
- spring.handlers
- spring.schemas
這三個配置文件的源文件都在/dubbo-config-spring/src/main/resources/META-INF/目錄下攘宙,打包后在dubbo-2.8.4.jar\META-INF目錄下可以看到這三個文件屯耸。
dubbo.xsd定義了擴展標簽的xsd文件。而spring.handlers和spring.schemas蹭劈,它們必須放在META-INF目錄中疗绣。Spring會默認去加載它們。加載完成后铺韧,再去加載xml配置文件時多矮,如果xml中使用了dubbo的自定義schema時,將會使用spring.handlers配置的handler來進行解析哈打。
spring.handlers中是自定義命名空間與自定義解析bean的映射內(nèi)容如下:
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
spring.schemas中是命名空間和xsd文件的映射塔逃,內(nèi)容如下:
http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd
2、xml解析的入口DubboNamespaceHandler及各個配置的解析器
在com.alibaba.dubbo.config.spring.schema包下定義了DubboNamespaceHandler和DubboBeanDefinitionParser料仗。
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
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 DubboBeanDefinitionParser(AnnotationBean.class, true));
}
}
"module"湾盗、"registry"等對應(yīng)xsd中的element name;DubboNamespaceHandler繼承了spring的NamespaceHandlerSupport立轧,spring可以通過這個bean來解析擴展xml格粪。
Spring啟動初始化的時候會加載解析xml配置文件,BeanDefinitionParserDelegate類的parseBeanDefinitions負責(zé)解析xml中的一級節(jié)點氛改,
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
當(dāng)xml元素的類型不屬于spring命名空間帐萎,即是自定義的,就會執(zhí)行delegate.parseCustomElement(ele);
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)會調(diào)用DefaultNamespaceHandlerResolver的resolve胜卤,通過uri來從META-INF/spring.handlers中取得對應(yīng)的解析器疆导,實例化,然后運行init葛躏,并將這個實例保存到handlerMappings中澈段,最后返回這個自定義handler實例。handler.parse會調(diào)用自定義的BeanDefinitionParser的pase來解析xml舰攒。
注意均蜜,dubbox的擴展點不能重復(fù)定義