基于JDK8API
https://docs.oracle.com/javase/8/docs/api/index.html
JAVA-Proxy
最近了解到了一个Netty + Spring + Zookeeper实现的RPC框架, 感觉很有意思. 于是开始学习Netty和相关的知识, 想自己也实现一个. 这就是这系列的第一章咯 - Java 动态代理.
1.简介
public class Proxy
extends Object
implements Serializable
在这个RPC框架中Java Proxy是一个非常重要的概念. 客户端靠Proxy来代理远程service服务器上的实现的服务, 可以使远程服务器上服务的调用就像在调用客户端本地的服务一样.
Java 的Proxy类提供了一些用于创建动态代理类和实例的静态方法, 同时它也是由这些方法创建出的类的超类.
以下是一个创建接口Foo代理的例子:
InvocationHandler handler = new MyInvocationHandler(...);
Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).
newInstance(handler);
// 简洁一点的方式
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class<?>[] { Foo.class },
handler);
动态代理类(dynamic proxy class)指的是实现了一系列在运类被创建的时候指定的接口的类.
代理接口(proxy interface)指的是被代理类实现的接口.
代理实例(proxy instance)指的是代理类的实例.
每个代理实例都和一个实现了InvocationHandler
接口的invocation handler 对象相关联. 在代理实例上调用其代理接口中定义的方法, 会被调转到该代理实例相关联的invocation handler 对象的invoke
方法上. 并向该invoke
方法传递三个参数: 该代理实例,代表被调用方法的java.lang.reflect.Method对象和一个传入的参数的数组.然后invocation handler 执行这个Method方法, 并把该方法返回的结果作为代理实例的方法调用的结果返回.
*interface InvocationHandler
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
2.特性
代理类有如下属性:
- 如果所有的代理接口都是
public
的, 那么该代理类就是public
,final
,not abstract
的 - 如果代理接口是
non-public
的, 那么该代理类就是non-public
,final
,not abstract
的 - 代理类都继承自java.lang.reflect.Proxy
- 代理类以相同的顺序精确地实现了在创建时指定的接口
- 代理类如果实现了一个
non-public
接口, 那么该代理类将被定义在与该接口相同的包中. 否则, 该类的包将是未被指定的. - 既然代理类实现了所有指定的接口, 所以在代理类对应的Class对象上调用
getInterfaces
方法时, 将会返回一个与创建时传入的接口顺序相同的接口数组. 调用getMethods
方法时将会返回一个包含了所有代理借口中定义的方法的Method数组. Proxy.isProxyClass
方法将会在传入由调用Proxy.getProxyClass
返回的class或者调用Proxy.newProxyInstance
返回的对象的class时返回true, 否则返回false.- 每个代理类都有一个
public
的构造函数, 该构造函数接收一个InvocationHandler
的参数. 比直接调用构造函数更好的方法是调用Proxy.newProxyInstance方法, 该方法结合了Proxy.getProxyClass
方法和构造函数来创建实例.
代理实例有如下属性:
- 对于一个代理实例proxy和它的代理接口之一(e.g. Foo),
proxy instanceof Foo 将会返回true.
(Foo) proxy 将会得到成功地转换 - 每个代理实例都和在它的构造函数中传入的invocation handler相关联. 静态方法
Proxy.getInvocationHandler
将会返回最为参数传入的代理实例相关联的invocation handler. - 在代理实例上调用的方法将被编码传递给invocation handler的
invoke
方法. - 在代理实例上调用像
hashCode
,equals
, 或者toString
这些在java.lang.Object中定义的方法会得到和上述代理接口中的方法相同方式的处理.
3.在多个接口中方法重复了的情况
有些时候可能在多个不同的接口中定义的某些方法拥有完全相同的名称和参数类型, 在这种情况下, 代理类的接口的顺序就变得非常重要了. 在代理实例上调用了一个在代理接口中重复了的方法时, 由于代理类无法分辨到底调用的是哪个接口的方法, 所以, 在这种情况下, 代理类就会把在代理接口中最前面的(foremost)接口的方法(不管是在接口中直接定义的还是继承自超类的)对应的Method对象传递给invoke
方法. 传递给invoke
方法的Method对象的声明类将是java.lang.Object
.
如果一个代理接口中包含了签名和java.lang.Object
中的hashCode
, equals
, toString
相同的方法, 当调用这些方法时,传递给invoke
方法的Method对象将把java.lang.Object
作为他的声明类. 换句话说, 在决定传递给invoke
方法的Method对象时, java.lang.Object
中的public
, non-final
方法在逻辑上优先于所有代理接口.
值得注意的是, 当调用一个有重复的方法时, invoke
方法只会抛出属于某个重复的方法throws中声明的checked异常. 如果调用的方法拍哦除了一个不属于任何重复方法的checked异常, 那么invoke
方法将抛出一个unchecked的UndeclaredThrowableException
.