动态代理回顾
- 几乎所有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));
}