上文分析了Dubbo服务发布的整体流程,但服务代理生成的具体细节介绍得还不是很详细。下面将会接着上文继续分析。上文介绍了服务代理生成的切入点,如下:

Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

这里的proxyFactory是在ServiceConfig中定义的,是final类型静态变量,赋值后无法进行修改。如下:

    private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

proxyFactory通过ExtensionLoader拓展机制进行加载。查看ProxyFactory接口源码如下:

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI; @SPI("javassist")
public interface ProxyFactory { /**
* create proxy.
*
* @param invoker
* @return proxy
*/
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker) throws RpcException; /**
* create invoker.
*
* @param <T>
* @param proxy
* @param type
* @param url
* @return invoker
*/
@Adaptive({Constants.PROXY_KEY})
<T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException; }

ProxyFactory接口有三个实现类,分别为JavassistProxyFactory、JdkProxyFactory、StubProxyFactoryWrapper。其中JavassistProxyFactory、JdkProxyFactory作为代理工厂,StubProxyFactoryWrapper实现了对代理工厂进行装饰的功能。在Dubbo中通过SPI配置默认的代理工厂为JavassistProxyFactory(具体是如何实现配置的,这里就不讲了,详见Dubbo SPI拓展机制)。

下面来重点看看JavassistProxyFactory代理工厂。

JavassistProxyFactory继承实现关系图如下:

JavassistProxyFactory源码非常简单,只有两个方法:

package com.alibaba.dubbo.rpc.proxy.javassist;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.bytecode.Proxy;
import com.alibaba.dubbo.common.bytecode.Wrapper;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory;
import com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker;
import com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler; public class JavassistProxyFactory extends AbstractProxyFactory { @SuppressWarnings("unchecked")
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
} public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper类不能正确处理带$的类名
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
}

其中getProxy是实现抽象类AbstractProxyFactory中的抽象方法。AbstractProxyFactory抽象类实现了ProxyFactory接口中getProxy方法,JdkProxyFactory也实现了抽象类AbstractProxyFactory中的getProxy抽象方法。Javassist与Jdk动态代理的共同部分被封装在父类AbstractProxyFactory中,具体的实现类只需负责实现代理生成过程的差异化部分。

其中getInvoker方法是在ProxyFactory接口中定义的,用于创建Invoker。getInvoker中代码很简单,直接返回一个匿名类。匿名类继承了AbstractProxyInvoker抽象类,AbstractProxyInvoker抽象类又实现了Invoker接口,即表明改匿名类实现了Invoker接口,匿名类是封装了服务提供者的调用者。

Invoker接口定义了一个泛型。定义的方法很简单,只有两个方法,如下:

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.Node;

public interface Invoker<T> extends Node {

    //获取服务对象接口
Class<T> getInterface(); //获取封装了服务的调用者
Result invoke(Invocation invocation) throws RpcException;
}

抽象类AbstractProxyInvoker实现了Invoker接口,AbstractProxyInvoker定义属性和构造方法如下:

    private final T proxy;

    private final Class<T> type;

    private final URL url;

    public AbstractProxyInvoker(T proxy, Class<T> type, URL url) {
if (proxy == null) {
throw new IllegalArgumentException("proxy == null");
}
if (type == null) {
throw new IllegalArgumentException("interface == null");
}
if (!type.isInstance(proxy)) {
throw new IllegalArgumentException(proxy.getClass().getName() + " not implement interface " + type);
}
this.proxy = proxy;
this.type = type;
this.url = url;
}
  • proxy:是final类型变量,指向服务提供者
  • type:是final类型变量,服务的接口类型
  • url:是final类型变量,携带服务地址、端口等多种信息的自定义URL对象

构造方法主要对输入参数做一些校验,然后将输入参数值直接赋值给定义属性,赋值后定义属性值将不可更改。

AbstractProxyInvoker也实现了invoker方法。方法内部很简单,直接通过RpcResult创建一个对象即可,创建RpcResult时的构建参数是通过方法doInvoke生成的。如下:

    public Result invoke(Invocation invocation) throws RpcException {
try {
return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));
} catch (InvocationTargetException e) {
return new RpcResult(e.getTargetException());
} catch (Throwable e) {
throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
}
}

这里的doInvoke方法是抽象方法,由子类实现。抽象方法定义如下:

    protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;

在ProxyFactory接口JavassistProxyFactory实现类中,getInvoker方法内部通过匿名内部类实现了doInvoke抽象方法。

Result接口主要定义了RPC 调用的相关方法,如下:

package com.alibaba.dubbo.rpc;

import java.util.Map;

public interface Result {

    Object getValue();

    Throwable getException();

    boolean hasException();

    Object recreate() throws Throwable;

    @Deprecated
Object getResult(); Map<String, String> getAttachments(); String getAttachment(String key); String getAttachment(String key, String defaultValue); }

RpcResult是对Result接口的一个实现。可看作对传入对象Object的一个包装,部分源码如下:

public class RpcResult implements Result, Serializable {

    private static final long serialVersionUID = -6925924956850004727L;

    private Object result;

    private Throwable exception;

    private Map<String, String> attachments = new HashMap<String, String>();

    public RpcResult() {
} public RpcResult(Object result) {
this.result = result;
} public RpcResult(Throwable exception) {
this.exception = exception;
} public Object recreate() throws Throwable {
if (exception != null) {
throw exception;
}
return result;
}
}

本节介绍了Dubbo服务发布的Invoker生成过程,下节将继续分析Dubbo服务发布的服务暴露过程



如果对您有帮助,不妨点个赞、关注一波

Dubbo源码学习--服务发布(ProxyFactory、Invoker)的更多相关文章

  1. Dubbo源码学习--服务发布(ServiceBean、ServiceConfig)

    前面讲过Dubbo SPI拓展机制,通过ExtensionLoader实现可插拔加载拓展,本节将接着分析Dubbo的服务发布过程. 以源码中dubbo-demo模块作为切入口一步步走进Dubbo源码. ...

  2. Dubbo源码学习--服务发布(DubboProtocol、Exporter)

    在Dubbo服务发布的整体流程一文中,只是分析了服务发布的整体流程,具体的细节还没有进一步分析.本节将继续分析服务暴露的过程.在ServiceConfig中通过一句话即可暴露服务,如下: Export ...

  3. Dubbo源码学习--服务是如何发布的

    相关文章: Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 ServiceBean ServiceBean 实现ApplicationListener接口监听Conte ...

  4. Dubbo源码学习--服务是如何引用的

    ReferenceBean 跟服务引用一样,Dubbo的reference配置会被转成ReferenceBean类,ReferenceBean实现了InitializingBean接口,直接看afte ...

  5. dubbo源码之服务发布与注册

    服务端发布流程: dubbo 是基于 spring 配置来实现服务的发布的,对于dubbo 配置文件中看到的<dubbo:service>等标签都是服务发布的重要配置 ,对于这些提供可配置 ...

  6. dubbo源码之四——服务发布二

    dubbo版本:2.5.4 2. 服务提供者暴露一个服务的详细过程 上图是服务提供者暴露服务的主过程: 首先ServiceConfig类拿到对外提供服务的实际类ref(如:HelloWorldImpl ...

  7. 2、Dubbo源码解析--服务发布原理(Netty服务暴露)

    一.服务发布 - 原理: 首先看Dubbo日志,截取重要部分: 1)暴露本地服务 Export dubbo service com.alibaba.dubbo.demo.DemoService to ...

  8. Dubbo源码学习文章目录

    目录 Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 Dubbo源码学习--注册中心分析 Dubbo源码学习--集群负载均衡算法的实现

  9. dubbo源码学习(四):暴露服务的过程

    dubbo采用的nio异步的通信,通信协议默认为 netty,当然也可以选择 mina,grizzy.在服务端(provider)在启动时主要是开启netty监听,在zookeeper上注册服务节点, ...

随机推荐

  1. System.getProperty参数大全

    System.getProperty()参数大全 #java.version                                    Java Runtime Environment v ...

  2. 老大哥在看着你!我国部署超2000万个AI监控系统

    原文:Big brother is watching you! China installs 'the world's most advanced video surveillance system' ...

  3. 1192: [HNOI2006]鬼谷子的钱袋

    1192: [HNOI2006]鬼谷子的钱袋 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3530  Solved: 2575[Submit][St ...

  4. two.js之实现动画效果

    一.什么是two.js? Two.js 是面向现代 Web 浏览器的一个二维绘图 API.Two.js 可以用于多个场合:SVG,Canvas 和 WebGL,旨在使平面形状和动画的创建更方便,更简洁 ...

  5. HDFS的接口(命令行接口和Java接口)--笔记

    HDFS 文件的系统访问的接口 1.Hadoop的shell命令脚本 hadoop fs -ls   列出某一个目录下的文件 hadoop fs -lsr 递归的方式列出所有文件 hadoop fs ...

  6. 【转载】以Java的视角来聊聊SQL注入

    以Java的视角来聊聊SQL注入 原创 2017-08-08 javatiku Java面试那些事儿 在大二就接触过sql注入,之前一直在学习windows逆向技术,认为web安全以后不是自己的从业方 ...

  7. Python+selenium打开网页

    东西都安装好了,是不是都迫不及待的想要运行一个程序呢? 不过不幸的是,在正式编程打开网页之前,我们还需要做一件事:下载驱动. 据说,在很久之前的selenium1和2中,驱动是被内嵌在selenium ...

  8. 六:Ioc和AOP使用拓展

    Ioc和AOP使用拓展 一:1.构造注入 一个<constructor-arg>元素表示构造方法的一个参数,且使用时不区分顺序,index指定元素,位置从0开始,Type用来指定参数,避免 ...

  9. Servlet编程实例2

    上次实验中利用HttpServletRespon.sendRedict()方法来实现页面的转跳,而这种重定向请求的方法无法传递缓存的内容. 所以为了做出改进,这次使用RequestDispatcher ...

  10. CLR设计类型之接口

    写在前面的话:             写到这一节的时候,CLR设计类型就已经结束了,因为CLR要求的是有一定基础的人看的,所以我们不是从基础类型以及运算符开始的,文章从一开始就讲的是深入面向对象编程 ...