dubbo Proxy Wrapper 代理类源码分析

本文深入分析了Dubbo中的动态代理机制,包括Wrapper和Proxy的创建过程。从JDK动态代理的基本原理出发,探讨了Dubbo如何使用Javassist生成代理类。内容涵盖Wrapper和Proxy的差异,以及它们在RPC调用中的作用,帮助理解Dubbo框架的内部运作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态代理回顾

  • 几乎所有java相关框架都需要采用动态代理工具; 【个人理解有以下两点】
    • 强类型:java是强类型语言,在编译阶段有类型检测
    • 切面编程:在用户代码执行之前、之后... 织入通用处理逻辑
  • jdk动态代理相关类有java.lang.reflect.Proxy,java.lang.reflect.InvocationHandler
  • jdk创建代理类需要实现InvocationHandler接口,并作为参数进一步可创建对应代理Proxy类
  • InvocationHandler :Object invoke(Object proxy, Method method, Object[] args) 只有一个接口方法, 通常我们只需实现该方法,不会显式调用,proxy对象会调用该方法
    • proxy:被代理对象 可进行嵌套如 proxy1 -> proxy2 -> ... -> originObject
    • 当调用proxy对象方法时,会调用到InvocationHandler#invoke 方法
  • jdk动态代理需要有接口定义;Javassist或asm 组件可以没有接口
  • 动态代理相关文档链接

概述

  • dubbo提供的动态代理工厂有JdkProxyFactory,JavassistProxyFactory
  • 通过ProxyFactory接口的@SPI注解可看到默认实现是JavassistProxyFactory
  • 使用Javassist生成代理类文件的核心类有 Proxy, Wrapper

dubbo 动态代理层的作用与必要性

  • 图中的rpc调用没画出序列化、网络层... 【本文忽略】

  • proxy-client,proxy-server仅表示RPC框架中代理层;具体创建的代理对象会有很多个

  • 客户端与服务端通常部署在不同服务器上,那么接口调用与实现需要发送网络请求

  • client端调用rpc方法时:

    • 可显示调用rpc接口每个方法,实际是调用代理类的方法【必须有rpc接口实现类代理】
    • method通常拆解为 类、方法名、参数、返回值类型...等数据
    • 任何接口方法在proxy中无非是ClassName,methodName,paramType, paramValue... 等数据;
    • 由具体接口方法 ---> 代理对象方法 ---> 封装方法数据为req ---> send req
  • server端接收到rpc方法请求时:

    • 解析将方法数据创建代理对象;
    • 为什么不创建具体的rpc接口实现类对象?框架代码在编写之时没法调用具体实现类的方法,只能调用代理类Proxy的方法;
    • Proxy在用户代码运行时采用动态反射引用到具体的实现类
    • receive req ---> 解析req数据 ---> 代理对象 ---> 接口实现类

dubbo动态代理

  • ProxyFactory默认采用JavassistProxyFactory实现
@SPI("javassist")
public interface ProxyFactory {

    // 阅读代码过程中 发现该方法在服务引用阶段调用多一些【有例外】
    // 若在服务引用阶段调用,通常需要根据引用rpc接口创建对应的invoker实例
    // 创建非范化调用代理对象
    @Adaptive({PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker) throws RpcException;

    // 阅读代码过程中 发现该方法在服务引用阶段调用多一些【有例外】
    // 若在服务引用阶段调用,通常需要根据引用rpc接口创建对应的invoker实例
    // generic: 控制是否范化代理对象
    @Adaptive({PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;

    // 阅读代码过程中 发现该方法在服务暴露阶段调用多一些【有例外】
    // proxy:rpc接口实现类,或其他...
    @Adaptive({PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;

}
  • JavassistProxyFactory 有出现两个核心类Proxy,Wrapper
  • 注意Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker)); 返回值不是Proxy类型
public class JavassistProxyFactory extends AbstractProxyFactory {

    @Override
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        // Proxy#getProxy 返回 Proxy对象        
        // 在调用newInstance方法传入 invoker【被代理对象】
        // 【重点】生成具体代理对象【具体代理类的 ClassType 不是 org.apache.dubbo.common.bytecode.Proxy】
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // 创建Wrapper包装类【请注意:不需要用到具体的 proxy引用,只需className即可】
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        // 创建Invoker对象,此处需要传入被代理对象proxy【如:rpc接口实现类】
        return new AbstractProxyInvoker<T>(proxy, type, url) {
           
            // 该方法类似与 InvocationHandler :Object invoke(Object proxy, Method method, Object[] args)
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                // AbstractProxyInvoker#invoke方法会调用doInvoke方法
                // doInvoke方法会调用wrapper#invokeMethod
                // wrapper#invokeMethod方法会调用proxy对应的方法
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

}

dubbo Wrapper代理类创建分析

  • testDemoProtocol方法中执行protocol.export(invoker) 方法时需要传入invoker
  • 该invoker通过proxyFactory.getInvoker(***)进行创建【通常proxyFactory.getInvoker 在服务暴露阶段会被调用到;也有例外】
  • 默认调用JavassistProxyFactory#getInvoker方法;进一步调用Wrapper相关方法
public class DubboProtocolTest {
    private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
    private ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
    
    @Test
    public void testDemoProtocol() throws Exception {
        // 接口实现对象【被代理对象】
        DemoService service = new DemoServiceImpl();
        int port = NetUtils.getAvailablePort();
        // 分析入口:服务暴露阶段调用 Proxy#getInvoker【有例外】
        protocol.export(proxyFactory.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:" + port + "/" + DemoService.class.getName() + "?codec=exchange")));
        service = proxyFactory.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:" + port + "/" + DemoService.class.getName() + "?codec=exchange").addParameter("timeout",
                3000L)));
        assertEquals(service.getSize(new String[]{"", "", ""}), 3);
    }
    。。。
}

 

org.apache.dubbo.common.bytecode.Wrapper

  • proxy.getInvoker(***) 会执行Wrapper#getWrapper方法
  • Wrapper#makeWrapper 主要调用javassist api 生成动态代理类
  • debug到 cc.toClass() 时手动运行cc.mCtc.writeFile("/doc"); 则会在/doc/** 相关目录看到Wrapper*.class 文件
public abstract class Wrapper {
    private static final Map<Class<?>, Wrapper> WRAPPER_MAP = new ConcurrentHashMap<Class<?>, Wrapper>(); //class wrapper map
    private static final String[] EMPTY_STRING_ARRAY = new String[0];
    private static final String[] OBJECT_METHODS = new String[]{"getClass", "hashCode", "toString", "equals"};
    private static AtomicLong WRAPPER_CLASS_COUNTER = new AtomicLong(0);
    
    // 此处的参数对象Class 通常是被代理【如:rpc接口实现累】对象的class
    public static Wrapper getWrapper(Class<?> c) {
       // 已经是动态代理类型则返回
        while (ClassGenerator.isDynamicClass(c)) // can not wrapper on dynamic class.
        {
            c = c.getSuperclass();
        }

        if (c == Object.class) {
            return OBJECT_WRAPPER;
        }
        // 会有缓存
        return WRAPPER_MAP.computeIfAbsent(c, key -> makeWrapper(key));
    }
    
    
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值