源码分析基于dubbo 2.6.0
从DubboNamespaceHandler可以看到,dubbo的标签都是由DubboBeanDefinitionParser解析。如
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
需要关注一下DubboBeanDefinitionParser对service标签的处理
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);
String id = element.getAttribute("id");
...
else if (ServiceBean.class.equals(beanClass)) {
String className = element.getAttribute("class");
if (className != null && className.length() > 0) {
RootBeanDefinition classDefinition = new RootBeanDefinition();
classDefinition.setBeanClass(ReflectUtils.forName(className));
classDefinition.setLazyInit(false);
parseProperties(element.getChildNodes(), classDefinition);
beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
}
}
这里的beanClass就是DubboBeanDefinitionParser创建时的参数,这里就是ServiceBean.class。
从上面代码可以看到,dubbo中的service都是ServiceBean, 如
<dubbo:service id="dubboHelloService" interface="com.dubbo.start.service.HelloService" class="com.dubbo.start.service.HelloServiceImpl">
则dubboHelloService解析为一个ServiceBean, ServiceBean中ref属性为HelloServiceImpl。(不要误以为dubboHelloService解析为HelloServiceImpl,这里和spring不一样)
ServiceBean实现了ApplicationListener
spring启动时,会触发ServiceBean.onApplicationEvent方法,最终调用到父类ServiceConfig.doExport方法
doExport方法前面花费了很大段代码进行参数检查,然后调用doExportUrls方法
private void doExportUrls() {
List<URL> registryURLs = loadRegistries(true);
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
对每一种协议protocol,都要doExportUrlsFor1Protocol。
我们一般使用dubbo protocol, <dubbo:protocol name="..." port="...">
doExportUrlsFor1Protocol 花了很多代码将interface,method,timeout,validation这些参数放到一个map中,然后组成一个url
String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = this.findConfigedPorts(protocolConfig, name, map);
URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
// 扩展
if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.hasExtension(url.getProtocol())) {
url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.getExtension(url.getProtocol()).getConfigurator(url).configure(url);
}
String scope = url.getParameter(Constants.SCOPE_KEY);
if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
// 暴露本地接口
if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
exportLocal(url);
}
// 暴露远程接口
if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
if (registryURLs != null && registryURLs.size() > 0) {
for (URL registryURL : registryURLs) {
url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
URL monitorUrl = loadMonitor(registryURL);
if (monitorUrl != null) {
url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
}
if (logger.isInfoEnabled()) {
logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
}
// 注册并暴露接口
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
} else {
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
}
}
this.urls.add(url);
暴露本地接口使用InjvmProtocol处理。
这里只关注远程接口的暴露。
registryURLs的格式为registry://(zk ip):(zk prot)/com.alibaba.dubbo.registry.RegistryService?registry=zookeeper&...
, 所以protocol.export
会调用RegistryProtocol.export。dubbo将上下文环境存放在url参数中,所以看dubbo源码也得分析它的url格式。
dubbo zookeeper的注册会在下一篇文章讲解。
网友评论