在xml上写一个dubbo标签就可以把远程的服务引用到本地使用:

<dubbo:reference id="buyFoodService" interface="com.test.dubbo.service.BuyFoodService"/>

既然用spring那就是Schema了,dubbo中自定义了Schema,在DubboNamespaceHandler中:

registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
spring 中继承BeanDefinitionParser 实现自定义解析xml的规则。DubboBeanDefinitionParser内实现了解析。
最终要生成一个对应class的BeanDefinition。BeanDefinition在spring中时bean的数据源。
各个标签对应的pojo:
public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(plicationConfig.class, true));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(duleConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(gistryConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(nitorConfig.class, true));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(oviderConfig.class, true));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(nsumerConfig.class, true));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(otocolConfig.class, true));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(rviceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ferenceBean.class, false));
registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(notationBean.class, true));
}
 
ReferenceBean的继承结构:
直接看afterPropertiesSet()方法,在spring中初始化好bean后会执行这个方法,方法中会注入各个组件:
ConsumerConfig,ApplicationConfig,List<RegistryConfig>,MonitorConfig,ModuleConfig,然后是这段代码:
        Boolean b = isInit();
if (b == null && getConsumer() != null) {
b = getConsumer().isInit();
}
if (b != null && b.booleanValue()) {
getObject();
}

这个init就是前面设置reference标签时的一个可选属性,如果我们设置true,那么在执行afterPropertiesSet()的时候就会执行到这个getObject()方法。

  public Object getObject() throws Exception {
return get();
}
这个getObject是FactoryBean的实现,这个在spring容器中,FactoryBean跟普通Bean不同,通过BeanFactory类的getBean方法直接获取到的并不是该FactoryBean的实例,而是该FactoryBean中方法getObject返回的对象。所以当我们执行BuyFoodService buyFoodService = (BuyFoodService) context.getBean("buyFoodService");
这样的代码是就也会执行到getObject()方法。
getObject()方法中调用的是父类ReferenceConfig的get();然后会调用到init()方法。
 public synchronized T get() {
if (destroyed) {
throw new IllegalStateException("Already destroyed!");
}
if (ref == null) {
init();
}
return ref;
}
看这个init方法,写得不怎么样,像膏药一样一块块散落着一地。先不管,直接看它返回的代码:
invoker = refprotocol.refer(interfaceClass, url);
// 创建服务代理
return (T) proxyFactory.getProxy(invoker);
首先protcol产生一个invoker,然后把这个invoker代理,来提供使用。
这个proxyFactory和Protocol我们看到是这样获取的:
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
ProxyFactory的代码:
@SPI("javassist")
public interface ProxyFactory {
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker) throws RpcException; @Adaptive({Constants.PROXY_KEY})
<T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}
Protocol的代码:
@SPI("dubbo")
public interface Protocol {
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy(); }
ProxyFactory用javassist作为默认实现,Protocol默认用dubbo,所以我们先关注两个子类:JavassistProxyFactory和DubboProtocol。
 
1,DubboProtocol的refer方法返回一个DubboInvoker。
2,JavassistProxyFactory把DubboInvoker作为被代理对象动态产生一个代理类
 
在生成Invoker的时候依次执行的是:ProtocolListenerWrapper,ProtocolFilterWrapper,RegistryProtocol,DubboProtocol。
在这个过程中需要像注册中心那信息,组装出存储service调用必要信息的实例。其中很多细节,后续自己研究。
 
那么ProxyFactory是JavassistProxyFactory,其实先执行的是StubProxyFactoryWrapper,在前面文章提到过了这种机制。StubProxyFactoryWrapper的构造函数参数是ProxyFactory,这里在上篇dubbo中Listener的实现中也有涉及到。他在getProxy里做了逻辑。
先了解下Stub(存根),在dubbo中远程调用一个服务被封装成一个本地service,一般我们都是引用接口,就可以调用到它的方法,实现则在远程的应用上,但当我们想在发起远程请求前做一些事情,比如做ThreadLocal缓存,提前验证参数,调用失败后伪造容错数据。这个就是stub要实现的事情。这段逻辑就在StubProxyFactoryWrapper的getProxy方法里。我们来看一下它代码:
public <T> T getProxy(Invoker<T> invoker) throws RpcException {
T proxy = proxyFactory.getProxy(invoker);
if (GenericService.class != invoker.getInterface()) {
// 查看有没有stub属性
String stub = invoker.getUrl().getParameter(Constants.STUB_KEY, invoker.getUrl().getParameter(Constants.LOCAL_KEY));
if (ConfigUtils.isNotEmpty(stub)) {
Class<?> serviceType = invoker.getInterface();
if (ConfigUtils.isDefault(stub)) {
if (invoker.getUrl().hasParameter(Constants.STUB_KEY)) {
stub = serviceType.getName() + "Stub";
} else {
stub = serviceType.getName() + "Local";
}
}
try {
Class<?> stubClass = ReflectUtils.forName(stub);
if (! serviceType.isAssignableFrom(stubClass)) {
throw new IllegalStateException("The stub implemention class " + stubClass.getName() + " not implement interface " + serviceType.getName());
}
try {
// 判断有没有参数是本service的构造函数,要有这个函数才能可用
Constructor<?> constructor = ReflectUtils.findConstructor(stubClass, serviceType);
// 这里看到是直接使用constructor,并没有判断,不是很好,看起来有问题,实际在解析stub属性的时候已经做过是否包含指定构造函数,如果没有责会报错。所以这里可以放心使用,不会空指针。
proxy = (T) constructor.newInstance(new Object[] {proxy});
//export stub service
URL url = invoker.getUrl();
if (url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT)){
url = url.addParameter(Constants.STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ","));
url = url.addParameter(Constants.IS_SERVER_KEY, Boolean.FALSE.toString());
try{
export(proxy, (Class)invoker.getInterface(), url);
}catch (Exception e) {
LOGGER.error("export a stub service error.", e);
}
}
} catch (NoSuchMethodException e) {
throw new IllegalStateException("No such constructor \"public " + stubClass.getSimpleName() + "(" + serviceType.getName() + ")\" in stub implemention class " + stubClass.getName(), e);
}
} catch (Throwable t) {
LOGGER.error("Failed to create stub implemention class " + stub + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);
// ignore
}
}
}
return proxy;
}

返回代理实例,所以在开头申明的BuyFoodService,在spring容器中实际指向的是一个封装好的代理。

dubbo refrence bean(服务引用)的更多相关文章

  1. 【DUBBO】Dubbo原理解析-服务引用

    服务引用是服务的消费方向注册中心订阅服务提供方提供的服务地址后向服务提供方引用服务的过程. 服务的应用方在spring的配置实例如下: <dubbo:referenceid="demo ...

  2. Dubbo(三):深入理解Dubbo源码之如何实现服务引用

    一.前言 前面讲了服务是如何导出到注册中心的.其实Dubbo做的一件事就是将服务的URL发布到注册中心上.那现在我们聊一聊消费者一方如何从注册中心订阅服务并进行远程调用的. 二.引用服务时序图 首先总 ...

  3. Dubbo源码(四) - 服务引用(消费者)

    前言 本文基于Dubbo2.6.x版本,中文注释版源码已上传github:xiaoguyu/dubbo 上一篇文章,讲了Dubbo的服务导出: Dubbo源码(三) - 服务导出(生产者) 本文,咱们 ...

  4. dubbo服务引用与集群容错

    服务引用无非就是做了两件事 将spring的schemas标签信息转换bean,然后通过这个bean的信息,连接.订阅zookeeper节点信息创建一个invoker 将invoker的信息创建一个动 ...

  5. 阿里面试:dubbo的服务引用过程

    点赞再看,养成习惯,微信搜一搜[三太子敖丙]关注这个喜欢写情怀的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系 ...

  6. Dubbo原理和源码解析之服务引用

    一.框架设计 在官方<Dubbo 开发指南>框架设计部分,给出了引用服务时序图: 另外,在官方<Dubbo 用户指南>集群容错部分,给出了服务引用的各功能组件关系图: 本文将根 ...

  7. Dubbo 源码分析 - 服务引用

    1. 简介 在上一篇文章中,我详细的分析了服务导出的原理.本篇文章我们趁热打铁,继续分析服务引用的原理.在 Dubbo 中,我们可以通过两种方式引用远程服务.第一种是使用服务直联的方式引用服务,第二种 ...

  8. dubbo源码分析02:服务引用

    一.何时创建服务引用 引用官方文档的原话,如果将Dubbo托管在Spring-IOC容器下,Dubbo服务引用的时机有两个,第一个是在Spring容器调用ReferenceBean的afterProp ...

  9. Dubbo服务引用源码解析③

    ​ 上一章分析了服务暴露的源码,这一章继续分析服务引用的源码.在Dubbo中有两种引用方式:第一种是服务直连,第二种是基于注册中心进行引用.服务直连一般用在测试的场景下,线上更多的是基于注册中心的方式 ...

随机推荐

  1. 51 Nod 1008 N的阶乘 mod P【Java大数乱搞】

    1008 N的阶乘 mod P 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 输入N和P(P为质数),求N! Mod P = ? (Mod 就是求模 %)   例如:n ...

  2. Quoit Design(最近点对+分治)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1007 Quoit Design Time Limit: 10000/5000 MS (Java/Oth ...

  3. echarts图表里label文字过长换行的方法

    在做一些图标时,有时会出现显示文字过长的问题,需要将其按照指定的字数换行,像下图这样 而echarts没有提供换行的方法,但是可以使用fomatter方法进行设置,代码如下 formatter: fu ...

  4. 慕课网-前端JavaScrpt基础面试技巧-学习笔记

    章节目录: JS基础知识(上)--讲解 JS 基础语法相关的面试题,分析原理以及解答方法.这一章节讲解了基础知识的第一部分:变量的类型和计算.以及JS "三座大山" -- 原型.作 ...

  5. Node.js系列-http

    前言: 最近一直忙着公司项目的事,战友们的留言也没空回复,博客也有段时间没有更新了,年底了就是一个的忙啊~~~(ps:同感的也给个赞吧) 现在前端的就是一直地更新一直有新的东西出来,什么ES2015, ...

  6. Maven中央仓库源地址改为阿里云(IDEA)

    我的Maven是IDEA2017.1.2集成的,所以settings.xml在此位置 E:\Program Files\JetBrains\IntelliJ IDEA 2017.1.2\plugins ...

  7. 如何实现dede首页栏目文章指定调用

    如何实现dede首页栏目文章指定调用,首页文章栏目指定ID调用 dobote | 浏览 11891 次 推荐于2016-05-20 10:53:26 最佳答案 {dede:arclist flag=' ...

  8. 解决导入MySQL数据库提示"Unknown character set: 'utf8mb4'"错误

    今天老左在准备迁移公司一个客户的网站到另外一台服务器中,根据正常的操作备份最新的网页文件和导出数据库,然后在新服务器中创建站点和数据库wget迁移进去解压.因为数据库比较小,所以直接用PHPMyAdm ...

  9. Xshell学习--菜鸟篇

    http://www.cnblogs.com/perseverancevictory/p/4910145.html 1)关于Xshell 网上更多的资料里提到的SSH客户端是putty,因为简单.开源 ...

  10. dedecms利用memberlist标签调用自定义会员模型的会员信息

    [摘要]本文讲一下dedecms如何利用memberlist标签调用自定义会员模型的会员信息. dedecms利用memberlist标签调用自定义会员模型的会员信息,这个问题找了很久,官方论坛提问过 ...