Dubbo与Spring整合

645 阅读3分钟

关于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的继承体系

image.png

这个图生成的有点意思~,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文件,内容为

http\://bubbo.apache.org/schema/bubbo=spring.schema.BubboNamespaceHandler

另外增加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());
    }

}

可能我们又想问,这个解析是什么时候触发的,简单,断点一下就知道了

image.png

关键的地方在

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);
	}
}

所以,整合的思路还是简单的