关于Dubbo的介绍和使用,还有框架的设计,官网dubbo.apache.org/zh/docs/v2.… (2.7版本)都有介绍,相信只要耐心一点,基本就能做到对Dubbo有个整体的了解。这里需要提一下的是,至少需要对Dubbo有基本了解,这样对看源码才不会一头雾水。
Dubbo的官方文档做得已经不错了,除了使用手册,还有介绍原理也有,甚至分析源码的也有。但是我觉得还是有不足,就是官网的分析很快就进入细节,看完后脑瓜还是嗡嗡的,我想缺少的是,对整体的把握,以及模块和模块间或者接口和接口之间的衔接。
接下来,我们了解的是Spring与Dubbo的整合,刚好上几篇文章是分析了Spring IoC原理,所以顺藤摸瓜,分析一下Spring IoC的扩展的应用,就是Dubbo的整合。
直至写文章,Dubbo已经更新到3.0.1了,而我的分析是基于2.6.x分支。
Dubbo模块的划分,做得还是挺好的,参考dubbo.apache.org/zh/docs/v2.… ,从模块里看到,与Spring整合的模块是dubbo-config-spring,里面有两个关键的类,一个是com.alibaba.dubbo.config.spring.ServiceBean,一个是com.alibaba.dubbo.config.spring.ReferenceBean,从名字可以看出,ServiceBean是跟服务导出和注册有关,ReferenceBean是跟服务引用有关,这里主要看ServiceBean。
ServiceBean类
先看一下ServiceBean的继承体系
这个图生成的有点意思~,ServiceBean实现ApplicationContextAware,所以可以注入ApplicationContext,实现ApplicationEventPublisherAware,所以可以发布事件,而本身又是ApplicationListener,所以又可以接收事件,另外还实现了DisposableBean和InitializingBean,在Bean初始化和销毁时,可以做一些扩展功能。
首先是afterPropertiesSet()方法
public void afterPropertiesSet() throws Exception {
// ...省略,跟加载配置有关,但跟这次无关
if (!isDelay()) {
export();
}
}
复制代码
如果不是延迟暴露服务,就会在afterPropertiesSet时暴露服务,但是我本地跑单测是,delay为空,所以isDelay()返回true,这里没有暴露服务,那就看onApplicationEvent(),这个是在刷新容器事件时触发
public void onApplicationEvent(ContextRefreshedEvent event) {
if (isDelay() && !isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
复制代码
这个方法比较简单,调的export()跟上面的是同一个方法,所以扩展整合也比较简单
这里还有个问题是,比如Dubbo的Bean是如何交给Spring IoC容器管理的
DubboNamespaceHandler类
这里举个例子,
在类路径下的META-INF目录下,增加spring.handlers文件,内容为
另外增加spring.schemas文件,内容为http://bubbo.apache.org/schema/bubbo/bubbo.xsd=META-INF/bubbo.xsd
最后增加bubbo.xsd,不了解xml schema,可以先去了解一下,不了解也不影响
<?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://bubbo.apache.org/schema/bubbo"
targetNamespace="http://bubbo.apache.org/schema/bubbo">
<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:element name="bubbo">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="protocol" type="xsd:string" />
<xsd:attribute name="version" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
复制代码
这里的意思是定义了bubbo的命名空间,这我们就可以在配置文件使用 <bubbo:protocol=”bubbo” version=”1.0.0″/>
关键还是要解析这些schema,比如BubboNamespaceHandler
public class BubboNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
System.out.println("BubboNamespaceHandler...");
}
}
复制代码
而DubboNamespaceHandler的配置解析,全由DubboBeanDefinitionParser,这个类实现了BeanDefinitionParser
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());
}
}
复制代码
可能我们又想问,这个解析是什么时候触发的,简单,断点一下就知道了
关键的地方在
DefaultBeanDefinitionDocumentReader
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;
// 这里决定是加载Spring的namespace,还是客户定制的namespace
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
复制代码
所以,整合的思路还是简单的