Dubbo之ProxyFactory

Stella981
• 阅读 631

概述

在分析服务暴露和服务引用的都提到ProxyFactory,它是Dubbo的代理工厂,只定义了两个方法。

  • getInvoker,暴露服务时调用,将ref(真正的服务实现类)转化为Invoker
  • getProxy,引用服务时调用,将Invoker对象转化为proxy代理对象

Invoker

Invoker 是 Dubbo 领域模型中非常重要的一个概念,很多设计思路都是向它靠拢。这就使得 Invoker 渗透在整个实现代码里,对于刚开始接触 Dubbo 的人,确实容易给搞混了。 下面我们用一个精简的图来说明最重要的两种 Invoker:服务提供 Invoker 和服务消费 Invoker:

Dubbo之ProxyFactory

上面是出自Dubbo的官方教程。

服务提供端的AbstractProxyInvoker,封装了真正的服务实现类,代理了它的方法。服务消费端的DubboInvoker等,封装了远程通信的逻辑。

Dubbo中对远程的调用是Invoker,调用服务真正的实现类也是Invoker。

ProxyFactory

Dubbo的代理工厂,Dubbo之所以能够宣传像调用本地服务一样调用远程服务,就是用它实现的。@SPI扩展接口,默认实现是JavassistProxyFactory,还有一个实现是JdkProxyFactory,最终都会被StubProxyFactoryWrapper包装。

@SPI("javassist")
public interface ProxyFactory {

    /**
     * 服务引用时调用
     * create proxy.
     *
     * @param invoker   Protocol生成的Invoker
     * @return proxy
     */
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker) throws RpcException;

    /**
     * 服务暴露时调用
     * create invoker.
     *
     * @param <T>
     * @param proxy Service对象
     * @param type  Service类型
     * @param url
     * @return invoker
     */
    @Adaptive({Constants.PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;

}

getInvoker

JavassistProxyFactory和JdkProxyFactory的返回的都是AbstractProxyInvoker,区别在于前者使用自动生成的类,包装了硬编码进行调用,后者则使用JDK反射机制进行调用。对于Dubbo来说都是Invoker,进行了统一的抽象,这样Dubbo框架就不需要关心服务的具体业务,全部交给AbstractProxyInvoker去封装。

真正调用服务的时候会执行Invoker#invoke(),而AbstractProxyInvoker会把调用转给真正的服务实现类。

JavassistProxyFactory

public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    // TODO Wrapper类不能正确处理带$的类名
    // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
    // proxy.getClass()返回的是实现类的Class,type是接口的Class
    // Wrapper对象对方法的调用进行了包装,没有使用反射,
    // Wrapper是使用javassist动态生成类才进行方法调用的包装
    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);
        }
    };
}

某个Wrapper类的代码片段,没有用反射

public Object invokeMethod(Object var1, String var2, Class[] var3, Object[] var4) throws InvocationTargetException {
    DemoService var5;
    try {
        var5 = (DemoService)var1;
    } catch (Throwable var8) {
        throw new IllegalArgumentException(var8);
    }

    try {
        if ("sayHello".equals(var2) && var3.length == 1) {
            return var5.sayHello((String)var4[0]);
        }
    } catch (Throwable var9) {
        throw new InvocationTargetException(var9);
    }

    throw new NoSuchMethodException("Not found method \"" + var2 + "\" in class cre.dubbo.demo.DemoService.");
}

JdkProxyFactory

public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    return new AbstractProxyInvoker<T>(proxy, type, url) {
        @Override
        protected Object doInvoke(T proxy, String methodName,
                                  Class<?>[] parameterTypes,
                                  Object[] arguments) throws Throwable {
            // 使用反射调用
            Method method = proxy.getClass().getMethod(methodName, parameterTypes);
            return method.invoke(proxy, arguments);
        }
    };
}

getProxy

生成的代理类实现指定的服务接口(还会实现EchoService接口),因此消费端在使用代理对象的时候跟使用本地服务是一样的。JavassistProxyFactory和JdkProxyFactory的都使用到了InvokerInvocationHandler

InvokerInvocationHandler它的作用是将消费端的调用转到内部的Invoker#invoke()上

JavassistProxyFactory

public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
    return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}

JdkProxyFactory

public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
    return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
}

InvokerInvocationHandler

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String methodName = method.getName();
    Class<?>[] parameterTypes = method.getParameterTypes();
    if (method.getDeclaringClass() == Object.class) {
        return method.invoke(invoker, args);
    }
    if ("toString".equals(methodName) && parameterTypes.length == 0) {
        return invoker.toString();
    }
    if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
        return invoker.hashCode();
    }
    if ("equals".equals(methodName) && parameterTypes.length == 1) {
        return invoker.equals(args[0]);
    }
    // Invoker进行Rpc调用
    return invoker.invoke(new RpcInvocation(method, args)).recreate();
}

附录

参照:https://dubbo.gitbooks.io/dubbo-dev-book/implementation.html

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java 复制Map对象(深拷贝与浅拷贝)
java复制Map对象(深拷贝与浅拷贝)CreationTime2018年6月4日10点00分Author:Marydon1.深拷贝与浅拷贝  浅拷贝:只复制对象的引用,两个引用仍然指向同一个对象
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Stella981 Stella981
3年前
Nepxion Discovery 5.5.0 发布
!(https://oscimg.oschina.net/oscnet/f81c043194ef4732880459d00c1a720e.png)发布日志功能更新:增加基于Opentracing调用链的支持,目前支持UberJaeger,实现在SpringCloudGateway、Zuul和服务上的灰度
Stella981 Stella981
3年前
Dubbo之服务消费原理
!(https://oscimg.oschina.net/oscnet/upa28a2508f9a95aa638dfaa1db5226f73b90.png)前言上篇文章《Dubbo之服务暴露》(https://my.oschina.net/u/3516086/blog/3186388"《Dubbo之服务暴露》")分析Dubbo服
Stella981 Stella981
3年前
SpringBoot开发案例之整合Dubbo提供者(二)
!00.jpg(https://blog.52itstyle.com/usr/uploads/2017/07/1329278006.jpg)大家有没有注意到,上一篇中提供者,暴露接口的方式?混搭。springboot本身接口实现使用了注解的方式,而Dubbo暴露接口使用的是配置文件的实现方式,即如下:代码importorg.s
Easter79 Easter79
3年前
SpringBoot开发案例之整合Dubbo提供者(二)
!00.jpg(https://blog.52itstyle.com/usr/uploads/2017/07/1329278006.jpg)大家有没有注意到,上一篇中提供者,暴露接口的方式?混搭。springboot本身接口实现使用了注解的方式,而Dubbo暴露接口使用的是配置文件的实现方式,即如下:代码importorg.s
Wesley13 Wesley13
3年前
unity将 -u4E00 这种 编码 转汉字 方法
 unity中直接使用 JsonMapper.ToJson(对象),取到的字符串,里面汉字可能是\\u4E00类似这种其实也不用转,服务器会通过类似fastjson发序列化的方式,将json转对象,获取对象的值就是中文但是有时服务器要求将传参中字符串中类似\\u4E00这种转汉字,就需要下面 publ
Stella981 Stella981
3年前
Dubbo之服务调用
概述Dubbo能够像调用本地服务一样调用远程服务,是依赖于Dubbo的代理机制。业务系统调用的服务方法,使用代理类,代理类里隐藏了远程通信的功能。代理对象会代理到InvokerInvocationHandler上,再调用它属性Invoker_invoke_()方法。这个Invoker是服务引用的过程中由Protocol创建的。比如