本文基于 Dubbo 2.6.1 版本,望知悉。
1. 概述
本文分享 Dubbo 动态代理的实现。
在 《Dubbo 用户指南 —— schema 配置参考手册》 中,我们可以看到 <dubbo:service />
和 <dubbo:reference />
标签中,可以通过 "proxy"
属性,可以配置动态代理的生成方式:
生成动态代理方式,可选:jdk / javassist
从说明中,我们可以看到,Dubbo 实现了两种方式生成代理:
- Javassit
- JDK
🙂 本文分享 Javassist 实现动态代理的源码;涉及代码如下:
dubbo-common
模块的bytecode
包。dubbo-rpc-api
模块的proxy
包。
下一篇文章分享 JDK 实现动态代理的源码。
2. 性能
在分享具体实现之前,可能有胖友对性能方面感兴趣,可以看看如下的内容:
- 《动态代理方案性能对比》
来自老徐的某篇文章
3. 整体流程
😈 瞎比比了这么多,我们开始进入正题了。相信很多胖友对动态代理的概念已经理解(如果暂时不理解,请 Google 下),那么 Dubbo 对它们使用在哪呢?见下图:
旁白君:本图暂不考虑集群容错、网络调用、序列化反序列等。
- 在 Dubbo 中,我们使用 Service 接口,作为服务 API 的契约。
在 Consumer 中,我们调用 Service 接口的方法时,实际调用的是 Dubbo 动态代理。下面先一起来看一个生成的 proxy 代码的示例:
1: package com.alibaba.dubbo.common.bytecode;
2:
3: import com.alibaba.dubbo.demo.DemoService;
4: import com.alibaba.dubbo.rpc.service.EchoService;
5: import java.lang.reflect.InvocationHandler;
6: import java.lang.reflect.Method;
7:
8: public class proxy0
9: implements ClassGenerator.DC, EchoService, DemoService
10: {
11: public static Method[] methods;
12: private InvocationHandler handler;
13:
14: public void bye(Object paramObject)
15: {
16: Object[] arrayOfObject = new Object[1];
17: arrayOfObject[0] = paramObject;
18: Object localObject = this.handler.invoke(this, methods[0], arrayOfObject);
19: }
20:
21: public String sayHello(String paramString)
22: {
23: Object[] arrayOfObject = new Object[1];
24: arrayOfObject[0] = paramString;
25: Object localObject = this.handler.invoke(this, methods[1], arrayOfObject);
26: return (String)localObject;
27: }
28:
29: public Object $echo(Object paramObject)
30: {
31: Object[] arrayOfObject = new Object[1];
32: arrayOfObject[0] = paramObject;
33: Object localObject = this.handler.invoke(this, methods[2], arrayOfObject);
34: return (Object)localObject;
35: }
36:
37: public proxy0() {}
38:
39: public proxy0(InvocationHandler paramInvocationHandler)
40: {
41: this.handler = paramInvocationHandler;
42: }
43: }- 该类通过
dubbo-common
模块的bytecode
模块的 Proxy 类,自动生成,使用 Javassist 技术。 - 生成的 proxy 类会实现我们定义的 Service 接口( 例如,此处是 DemoService )。
#bye(Object)
和#sayHello(Object)
方法,是我们定义在 DemoService 的接口方法,在生成的 proxy 类中,实现这些定义在接口中的方法,收拢统一调用java.lang.reflect.InvocationHandler#invoke(proxy, method, args)
方法。通过这样的方式,可以调用到最终的Invoker#invoke(Invocation)
方法,实现 RPC 调用。- 注意,此处我们一直用的 proxy 一直是小写的,这是为什么呢?请见下文大写的 Proxy 类。
- 该类通过
在 Provider 中,XXXProtocol 会获得被调用的 Exporter 对象,从而获得到 Invoker 对象。但是呢,Invoker 对象实际和 Service 实现对象,是无法直接调用,需要有中间的一层 Wrapper 来代理分发到 Service 对应的方法。下面我们来看一个生成的 Wrapper 代码的示例:
1: package com.alibaba.dubbo.common.bytecode;
2:
3: import com.alibaba.dubbo.demo.provider.DemoDAO;
4: import com.alibaba.dubbo.demo.provider.DemoServiceImpl;
5: import java.lang.reflect.InvocationTargetException;
6: import java.util.Map;
7:
8: public class Wrapper1
9: extends Wrapper
10: implements ClassGenerator.DC
11: {
12: public static String[] pns;
13: public static Map pts;
14: public static String[] mns;
15: public static String[] dmns;
16: public static Class[] mts0;
17: public static Class[] mts1;
18: public static Class[] mts2;
19:
20: public String[] getPropertyNames()
21: {
22: return pns;
23: }
24:
25: public boolean hasProperty(String paramString)
26: {
27: return pts.containsKey(paramString);
28: }
29:
30: public Class getPropertyType(String paramString)
31: {
32: return (Class)pts.get(paramString);
33: }
34:
35: public String[] getMethodNames()
36: {
37: return mns;
38: }
39:
40: public String[] getDeclaredMethodNames()
41: {
42: return dmns;
43: }
44:
45: public void setPropertyValue(Object paramObject1, String paramString, Object paramObject2)
46: {
47: DemoServiceImpl w;
48: try
49: {
50: w = (DemoServiceImpl)paramObject1;
51: }
52: catch (Throwable localThrowable)
53: {
54: throw new IllegalArgumentException(localThrowable);
55: }
56: if (paramString.equals("test01"))
57: {
58: w.test01 = ((String)paramObject2);
59: return;
60: }
61: if (paramString.equals("demoDAO"))
62: {
63: localDemoServiceImpl.setDemoDAO((DemoDAO)paramObject2);
64: return;
65: }
66: throw new NoSuchPropertyException("Not found property \"" + paramString + "\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.");
67: }
68:
69: public Object getPropertyValue(Object paramObject, String paramString)
70: {
71: DemoServiceImpl w;
72: try
73: {
74: w = (DemoServiceImpl)paramObject;
75: }
76: catch (Throwable localThrowable)
77: {
78: throw new IllegalArgumentException(localThrowable);
79: }
80: if (paramString.equals("test01")) {
81: return localDemoServiceImpl.test01;
82: }
83: throw new NoSuchPropertyException("Not found property \"" + paramString + "\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.");
84: }
85:
86: public Object invokeMethod(Object paramObject, String paramString, Class[] paramArrayOfClass, Object[] paramArrayOfObject)
87: throws InvocationTargetException
88: {
89: DemoServiceImpl w;
90: try
91: {
92: w = (DemoServiceImpl)paramObject;
93: }
94: catch (Throwable localThrowable1)
95: {
96: throw new IllegalArgumentException(localThrowable1);
97: }
98: try
99: {
100: if ("sayHello".equals(paramString) && paramArrayOfClass.length == 1) {
101: return w.sayHello((String)paramArrayOfObject[0]);
102: }
103: if ("bye".equals(paramString) && paramArrayOfClass.length == 1)
104: {
105: w.bye((Object)paramArrayOfObject[0]);
106: return null;
107: }
108: if ("setDemoDAO".equals(paramString) && paramArrayOfClass.length == 1)
109: {
110: w.setDemoDAO((DemoDAO)paramArrayOfObject[0]);
111: return null;
112: }
113: }
114: catch (Throwable localThrowable2)
115: {
116: throw new InvocationTargetException(localThrowable2);
117: }
118: throw new NoSuchMethodException("Not found method \"" + paramString + "\" in class com.alibaba.dubbo.demo.provider.DemoServiceImpl.");
119: }
120: }- 该类通过
dubbo-common
模块的bytecode
模块的 Wrapper 类,自动生成,使用 Javassist 技术。 - 不同于生成的 proxy类,不实现 Service 接口类,而是在
#invokeMethod(paramObject, paramString, paramArrayOfClass, paramArrayOfObject)
方法,提供给Invoker#invoke(invocation)
中调用,统一分发请求到 Service 对应的方法。从职能上来看,有一点像硬编码的 Controller 。 - 一个生成的 Wrapper类,只对应一个 Service ,从第 75 行的代码,我们也可以看出。
- 该类通过
4. ProxyFactory
com.alibaba.dubbo.rpc.ProxyFactory
,代理工厂接口。
在 《精尽 Dubbo 源码分析 —— 核心流程一览》 的 「4.5 ProxyFactory」,已经分享,胖友点击查看。
4.1 AbstractProxyFactory
com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory
,实现 ProxyFactory 接口,代理工厂抽象类。代码如下:
1: public abstract class AbstractProxyFactory implements ProxyFactory { |
可以看到,该抽象类,主要是实现了
#getProxy(invoker)
方法,获得需要生成代理的接口们。- 第 5 至 17 行: TODO 8022 芋艿
第 18 至 21 行:在原有 Invoker 对应关联的 Service 接口之上,增加 EchoService 接口。
FROM 《Dubbo 用户指南 —— 回声测试》
回声测试用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否通畅,可用于监控。
所有服务自动实现 EchoService 接口,只需将任意服务引用强制转型为 EchoService,即可使用。第 22 行:调用
#getProxy(invoker, types)
抽象方法,获得 Proxy 对象。
4.2 StubProxyFactoryWrapper
com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
,实现 ProxyFactory 接口,Stub 代理工厂 Wrapper 实现类,基于 Dubbo SPI Wrapper 机制加载。
🙂 该类,不在本文的范畴内,感兴趣的胖友可以先看下 《Dubbo 用户指南 —— 本地存根》 。后续,我们单独开文章分享。
4.3 JavassistProxyFactory
com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
,实现 AbstractProxyFactory 抽象类,基于 Javassist 代理工厂实现类。代码如下:
1: public class JavassistProxyFactory extends AbstractProxyFactory { |
#getProxy(invoker, interfaces)
方法- 第 5 行:调用
Proxy#getProxy(interface)
方法,获得 Proxy 对象。 - 第 5 行:调用
Proxy#newInstance(InvocationHandler)
方法,获得 proxy 对象。其中传入的参数是 InvokerInvocationHandler 类,通过这样的方式,让 proxy 和真正的逻辑代码解耦。- Proxy 和 proxy ,在 「7.3 Proxy」 中,详细解析。
- InvokerInvocationHandler ,在 「5. InvokerInvocationHandler」 中,详细解析。
- 第 5 行:调用
#getInvoker(proxy, type, url)
方法- 第 11 行:调用
Wrapper#getWrapper(Class<?>)
方法,获得 Wrapper 对象。- Wrapper ,在 「7.4 Wrapper」 中,详细解析。
- 第 12 至 19 行:创建 AbstractProxyInvoker 对象,实现
#doInvoke(...)
方法。在该方法中,调用Wrapper#invokeMethod(...)
方法,从而调用 Service 的方法。- AbstractProxyInvoker ,在 「6. AbstractProxyInvoker」 中,详细解析。
- 第 11 行:调用
5. InvokerInvocationHandler
com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler
,实现 java.lang.reflect.InvocationHandler
接口,代码如下:
1: public class InvokerInvocationHandler implements InvocationHandler { |
invoker
属性,Invoker 对象,用于在#invoke()
方法调用。#invoke(proxy, method, args)
实现方法,核心逻辑是调用Invoker#invoke(invocation)
方法,进行 RPC 调用。- 第 16 至 18 行:处理
#wait()
#notify()
等方法,进行反射调用。 - 第 20 至 28 行:处理
#toString()
#hashCode()
等方法,使用 Invoker 对象的方法,不进行 RPC 调用。 - 第 30 行:调用
Invoker#invoke(invocation)
方法,核心逻辑,进行 RPC 调用。 第 30 行:调用
Result#recreate()
方法,回放调用结果。代码如下:// RpcResult.java
public Object recreate() throws Throwable {
// 有异常,抛出异常
if (exception != null) {
throw exception;
}
// 无异常,返回结果
return result;
}- x
- 第 16 至 18 行:处理
🙂 通过 InvokerInvocationHandler ,可以实现 Proxy 和真正的逻辑解耦。
6. AbstractProxyInvoker
com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker
,实现 Invoker 接口,代理 Invoker 对象的抽象类。
6.1 属性
/** |
🙂 胖友,请看代码上的注释。
6.2 invoke
1: public Result invoke(Invocation invocation) throws RpcException { |
第 3 行:调用
#doInvoke(..)
抽象方法,执行调用,返回调用结果。#doInvoke(...)
方法,代码如下:/**
* 执行调用
*
* @param proxy 代理的对象
* @param methodName 方法名
* @param parameterTypes 方法参数类型数组
* @param arguments 方法参数数组
* @return 调用结果
* @throws Throwable 发生异常
*/
protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;第 3 行:创建 RpcResult 对象,将结果包装返回。
- 第 5 行:发生 InvocationTargetException 异常,创建 RpcResult 对象包装。
7. bytecode
在 dubbo-common
模块的 bytecode
包,基于 Javassit 库,动态编译,实现提供了通用的的动态代理实现。所以本小节,从动态编译的角度上来看,在内容上,和 《精尽 Dubbo 源码分析 —— 动态编译(一)之 Javassist》 有一定的相似。
7.1 ClassGenerator
com.alibaba.dubbo.common.bytecode.ClassGenerator
,类生成器,基于 Javassist 实现。
笔者在 《TCC-Transaction 源码分析 —— Dubbo 支持》 的 「2.1.3 TccProxy & TccClassGenerator」 已经详细分享,基本类似,胖友瞅瞅噢。
7.3 Proxy
com.alibaba.dubbo.common.bytecode.Proxy
,代理抽象类,用于创建 Proxy 和 proxy 对象。
笔者在 《TCC-Transaction 源码分析 —— Dubbo 支持》 的 「2.1.3 TccProxy & TccClassGenerator」 已经详细分享,基本类似,胖友瞅瞅噢。
如下是一个生成的 Proxy 代码的示例,代码如下:
public class Proxy0 extends Proxy implements ClassGenerator.DC { |
- 生成的Proxy 实现 Proxy 抽象类,是创建 生成的proxy 的工厂(一一对应)。例如,Proxy0 创建 proxy0 对象。
#newInstance(InvocationHandler)
方法,创建 生成的proxy 的方法。
7.4 Wrapper
com.alibaba.dubbo.common.bytecode.Wrapper
,Wrapper 抽象类,用于创建某个对象的方法调用的包装器,以避免反射调用,提高性能。即:
// 反射 |
- 为什么会提高性能呢?看到上文的 Wrapper 代理的示例,相信胖友已经明白。
7.4.1 抽象方法
在自动生成 Wrapper 类时,需要实现如下抽象方法:
abstract public String[] getPropertyNames(); |
7.4.2 getWrapper
#getWrapper(c)
方法,根据指定类,获得 Wrapper 对象。代码如下:
1: public static Wrapper getWrapper(Class<?> c) { |
- 第 3 至 4 行:判断是否已经继承了 ClassGenerator.DC.class ,如果是,拿到父类,避免重复包装。
- 第 7 至 8 行:若指定类为 Object.class ,返回 OBJECT_WRAPPER 对象。
第 11 行:从缓存
WRAPPER_MAP
中,获得 Wrapper 对象。WRAPPER_MAP
代码如下:/**
* Wrapper 对象缓存
* key :Wrapper 类。
* value :Proxy 对象
*/
private static final Map<Class<?>, Wrapper> WRAPPER_MAP = new ConcurrentHashMap<Class<?>, Wrapper>(); //class wrapper map第 13 至 16 行:调用
#makeWrapper(Class<?>)
方法,创建 Wrapper 对象,并添加到缓存。
7.4.3 makeWrapper
#makeWrapper(Class<?>)
方法,创建 Wrapper 对象。代码如下:
旁白君:实现上,和 Proxy 差不过的,只生成一个 Wrapper 类。
1: private static Wrapper makeWrapper(Class<?> c) { |
上文生成的 Wrapper1 的代码,对应的 DempServiceImpl 的代码如下:
1: public class DemoServiceImpl implements DemoService {
2:
3: /**
4: * 测试属性,{@link com.alibaba.dubbo.common.bytecode.Wrapper}
5: */
6: public String test01;
7:
8: private DemoDAO demoDAO;
9:
10: public String sayHello(String name) {
11: System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
12: return "Hello " + name + ", response form provider: " + RpcContext.getContext().getLocalAddress();
13: }
14:
15:
16: public void bye(Object o) {
17: System.out.println(JSON.toJSONString(o));
18: System.out.println(o.getClass());
19: }
20:
21: public void setDemoDAO(DemoDAO demoDAO) {
22: this.demoDAO = demoDAO;
23: }
24:
25: }- 下面,我们的解析,会结合这个类一起讲。
- ========== 生成代码 ==========
- 第 11 至 16 行:创建方法的开头的代码:
#setPropertyValue(o, n, v)
的【Wrapper1 第 45 至 46 行】#getPropertyValue(o, n)
的【Wrapper1 第 69 至 70 行】#invokeMethod(o, n, p, v)
的【Wrapper1 第 86 至 88 行】
- 第 19 至 21 行:设置方法的被调用对象的类型转换的代码:
#setPropertyValue(o, n, v)
的【Wrapper1 第 47 至 55 行】#getPropertyValue(o, n)
的【Wrapper1 第 71 至 79 行】#invokeMethod(o, n, p, v)
的【Wrapper1 第 89 至 97 行】
- 第 23 至 30 行:声明
pts
ms
mn
dmns
变量。🙂 每个变量的用途,已经添加到代码的注释上。 - 第 32 至 44 行:循环 public 属性,添加每个属性的设置和获得分别代码:
- 【DemoServiceImpl 第 6 行】
#setPropertyValue(o, n, v)
的【Wrapper1 第 56 至 60 行】#getPropertyValue(o, n)
的【Wrapper1 第 80 至 62 行】
- 第 46 至 106 行:设置方法
#invokeMethod(o, n, p, v)
的调用代码:- 【DemoServiceImpl 第 10 至 23 行】
- 【Wrapper1 第 98 至 119 行】
- 第 108 至 131 行:循环 setting / getting 属性,添加每个属性的设置和获得分别代码:
#setPropertyValue(o, n, v)
- 【DemoServiceImpl 第 21 至 23 行】【DemoServiceImpl 第 8 行】
- 【Wrapper1 第 61 至 62 行】
- 🙂
#getPropertyValue(o, n)
没举例子,胖友自己看代码脑补。
- ========== 生成类 ==========
- 第 138 行:创建 ClassGenerator 对象。
- 第 140 行:设置类名。
- 第 142 行:设置父类为 Wrapper.class
- 第 145 行:添加构造方法,参数为空
- 第 146 至 156 行:添加静态属性
pns
pts
mns
dmns
mts
。 - 第 158 至 174 行:添加抽象方法的实现。
- 第 178 行:生成类。
- ========== 创建对象 ==========
- 第 179 至 187 行:反射,设置静态属性的值。
- 第 189 行:创建 Wrapper 对象。
- ========== 释放 ==========
- 第 195 至 199 行:释放资源。
666. 彩蛋
趁着我球结婚的日子,写完了小文一篇。
祝福我球。
感慨我们离张牙舞爪的中二少年越来越远,身旁站着妻子或未婚妻,手上抱着一脸懵逼的孩子。