Dubbo进阶(十)- Dubbo动态对象生成过程详细分析

上一篇文章仔细的分析了 ,由 refer 方法 获取的 代理对象的过程,以及最后获取到的代理对象的结构,但是结尾比较草,本节仔细分析 字节码生成的代理对象。

结构

上一篇文章结尾草草的给出了 代理对象的结构图,但是并没有对(T) PROXY_FACTORY.getProxy(invoker)进行 整体上的分析:
在这里插入图片描述
首先在 Dubbo 的 ProxyFactory中,有两个字节码工具类,一个是JdkProxyFatory,另一个是 JavassistProxyFactory
具体SPI 文件如下:

stub=org.apache.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
jdk=org.apache.dubbo.rpc.proxy.jdk.JdkProxyFactory
javassist=org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory

并且默认使用的是 JavassistProxyFactory 作为字节码工具。
StubProxyFactoryWrapper 则主要是要执行优先于 ProxyFactory 的一些逻辑,这里主要是在 getProxy 对 配置了 stublocal 属性 进行一些额外处理。

在 JavassistProxyFactory中的构造方法如下:

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

上面构造方法主要是使用字节码工具,将多个 interfaces对象(包括了 EchoService)生成一个代理对象,再使用 InvokerInvocationHandler 包装起来。

ProxyFactory$Adaptive 源码

通过一定方式,获取到了 ProxyFactory$Adaptive 源码,便于我们后面分析:

package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {
    public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException {
        if (arg2 == null) throw new IllegalArgumentException("url == null");
        org.apache.dubbo.common.URL url = arg2;
        String extName = url.getParameter("proxy", "javassist");
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getInvoker(arg0, arg1, arg2);
}
    public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0, boolean arg1) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = url.getParameter("proxy", "javassist");
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getProxy(arg0, arg1);
}
    public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = url.getParameter("proxy", "javassist");
        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
        return extension.getProxy(arg0);
}
}

PROXY_FACTORY.getProxy(invoker) 详解

AbstractProxyFactory 代码示例:


    @Override
    public <T> T getProxy(Invoker<T> invoker) throws RpcException {
    // 不是泛化调用
        return getProxy(invoker, false);
    }

    @Override
    public <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException {
        Class<?>[] interfaces = null;
        String config = invoker.getUrl().getParameter(INTERFACES);
        if (config != null && config.length() > 0) {
            String[] types = COMMA_SPLIT_PATTERN.split(config);
            if (types != null && types.length > 0) {
                interfaces = new Class<?>[types.length + 2];
                interfaces[0] = invoker.getInterface();
                interfaces[1] = EchoService.class;
                for (int i = 0; i < types.length; i++) {
                    // TODO can we load successfully for a different classloader?.
                    interfaces[i + 2] = ReflectUtils.forName(types[i]);
                }
            }
        }
        if (interfaces == null) {
        // 加入EchoService.class
            interfaces = new Class<?>[]{invoker.getInterface(), EchoService.class};
        }

        if (!GenericService.class.isAssignableFrom(invoker.getInterface()) && generic) {
        // 加入GenericService.class
            int len = interfaces.length;
            Class<?>[] temp = interfaces;
            interfaces = new Class<?>[len + 1];
            System.arraycopy(temp, 0, interfaces, 0, len);
            interfaces[len] = com.alibaba.dubbo.rpc.service.GenericService.class;
        }

        return getProxy(invoker, interfaces);
    }

以下将一步一步,钻进getProxy 看其具体实现:

  1. ProxyFactory$Adaptive,会先检查参数。
  2. 通过proxy 参数获取使用的代理工厂,没有则使用默认的 javassist,使用 ExtensionLoader 的 SPI 方式获取。
  3. Adaptive 类中,获取的 extension 为 使用 StubProxyFactoryWrapper 包装后的 ProxyFactory,所以会先执行StubProxyFactoryWrappergetProxy 方法,而是否会执行 具体 ProxyFactorygetProxy 方法,则需要看 Wrapper 类的逻辑。
    在这里插入图片描述
  4. 进入 StubProxyFactoryWrappergetProxy,最终会先调用 AbstractProxyFactory,这里面的getProxy 方法主要是判断是否需要往代理生成类中再加入 GenericService 用于泛化调用。
  5. 而后调用子类的getProxy,这里使用 JavassistProxyFactory 为例:
    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }
  1. 使用字节码工具,将传入的interfaces类,以及 参数 InvokerInvocationHandler,动态生成一个新的代理类并返回。
    这里为什么是多个呢?在使用Dubbo 的EchoService 及 泛化调用时,都是使用强转的形式,所以这里就传入多个interfaces,才保证了强转不会出错。
  2. 在使用 javassist 构造 Proxy.getProxy 时候,截取了字节码class 反编译后的 java 代码,可以便于理解
    在具体的 JavassistProxyFactorypublic <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) 依次会生成两个类:一个是Proxy0的包装类,另一个是 proxy0,即具体功能存放类。

proxy0 代理类

public class proxy0 implements DC, EchoService, HelloService {
    public static Method[] methods;
    private InvocationHandler handler;

    public String hello(String var1) {
        Object[] var2 = new Object[]{var1};
        Object var3 = this.handler.invoke(this, methods[0], var2);
        return (String)var3;
    }

    public Object $echo(Object var1) {
        Object[] var2 = new Object[]{var1};
        Object var3 = this.handler.invoke(this, methods[1], var2);
        return (Object)var3;
    }

    public proxy0() {
    }

    public proxy0(InvocationHandler var1) {
        this.handler = var1;
    }
}

Proxy0 代理类

public class Proxy0 extends Proxy implements DC {
    public Object newInstance(InvocationHandler var1) {
        return new proxy0(var1);
    }

    public Proxy0() {
    }
}

分析在 JavassistProxyFactory 中的 以下方法:

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

从上面代码中可以有以下收获:

  1. Proxy.getProxy(interfaces) 返回的是一个 Proxy0 对象,注意是大写的P
  2. 最后调用 Proxy0newInstance 方法,实例化一个包装类型的 proxy0,注意是小写的
  3. dubbo 里面的 EchoService 或者 泛化调用,都是通过javassist 字节码工具,以实现方法形式实现,所以才可以在业务层面强转成功。
  4. 对于返回的代理类,也实现对应 refer 的接口,例如上述实现的 HelloService
  5. DC 是dynamic code 缩写,只是一个标识,说明是动态生成的类。
  6. 对于手动执行方法,都是调用 InvokerInvocationHandlerinvoke 方法

而对于 InvokerInvocationHandler 中invoker 及后面调用逻辑,且看下一篇文章分析。

觉得博主写的有用,不妨关注博主公众号: 六点A君。
哈哈哈,Dubbo小吃街不迷路:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值