源码分析基于dubbo 2.7.1
从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方法
public synchronized void export() {
// 检查和发布Config
checkAndUpdateSubConfigs();
if (!shouldExport()) {
return;
}
if (shouldDelay()) { // 延迟Export
delayExportExecutor.schedule(this::doExport, delay, TimeUnit.MILLISECONDS);
} else {
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="...">
loadRegistries
获取dubbo loadRegistries配置,并生成registry,格式为registry://...
doExportUrlsFor1Protocol 花了很多代码将interface,method,timeout,validation这些参数放到一个map中,然后组成一个url
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
...
// 暴露服务
String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = this.findConfigedPorts(protocolConfig, name, map);
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(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);
// scope为none不暴露服务
if (!Constants.SCOPE_NONE.equalsIgnoreCase(scope)) {
// 暴露本地服务
if (!Constants.SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url);
}
// 暴露远程服务
if (!Constants.SCOPE_LOCAL.equalsIgnoreCase(scope)) {
if (CollectionUtils.isNotEmpty(registryURLs)) {
for (URL registryURL : registryURLs) {
url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));
// 获取监控配置
URL monitorUrl = loadMonitor(registryURL);
if (monitorUrl != null) {
url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
}
// 获取代理配置
String proxy = url.getParameter(Constants.PROXY_KEY);
if (StringUtils.isNotEmpty(proxy)) {
registryURL = registryURL.addParameter(Constants.PROXY_KEY, proxy);
}
// 生成Invoker
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 {
...
}
...
}
}
this.urls.add(url);
}
暴露本地接口使用InjvmProtocol处理。
本文只关注远程接口的暴露。
registryURLs的格式为registry://...
, 所以<服务注册> 会调用RegistryProtocol.export。
dubbo zookeeper的注册会在下一篇文章讲解。
网友评论