代理模式
在讲动态代理之前,需要了解什么是代理模式,经典的代理模式的uml图相信大家都见过:
代理模式
RealSubject是需要被代理的对象。我们要在RealSubject之外增加一些处理,就增加一个Proxy类,实现Subject接口,在Proxy类里持有RealSubject的实例。
Client的请求全部打到Proxy实例上,由Proxy实例来控制是否将请求转发给RealSubject,或者做额外的处理。
代理模式的优点:对于外界来讲,完全无感知, 耦合性 低。
看一下实例代码:
这里对Integer类做了一层代理。这个类的作用在于,在compareTo请求之上增加了信息的打印输出。
在实际场景中,如果我们需要对多个方法都要做类似的处理,在每个地方都增加同样的代码,就显得有点不够优雅了。这时候,可以使用 java 的动态代理。
常用的动态代理有两种实现方式: JDK 和 CGLIB
JDK动态代理
看名字就可以知道,这种动态代理是JDK本身就支持的,需要借助java. lang .reflect下的接口来实现。
创建代理对象
要想创建一个代理对象,需要使用Proxy类的newProxyInstance方法。
这个方法有三个参数:
类加载器。作为Java安全模型的一部分,对于系统类和从因特网上下载下来的类,可以使用不同的类加载器。
Class对象数组,每个元素都是需要实现的接口。
调用处理器。
调用处理器
调用处理器是实现了InvocationHandler接口的类对象。在这个接口中只有一个方法:
动态代理类,需要实现此接口,在接口方法的实现里做代理逻辑的处理。
创建动态代理
创建一个动态代理对象的工作如下:
获取 RealSubject上的所有接口列表;
确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX ;
根据需要实现的接口信息,在代码中动态创建 该Proxy类的 字节码 ;
4 将对应的字节码转换为对应的class 对象;
创建InvocationHandler 实例handler,用来处理Proxy所有方法调用;
Proxy的class对象以创建的handler对象为参数, 实例化 一个proxy对象。
接下来我们看一段代码实现:
那如何TraceHandler有什么用呢?
利用TraceHandler可以打印出 二分查找 的查找顺序:
为何 toString 也会被代理
细心的读者会发现,上面的程序执行最后有一行
为什么toString方法也会被代理呢,我们代理的接口里,并没有声明toString的方法呀。
这里有两点我们需要知道:
所有的代理类都扩展于Proxy类;
所有的代理类都覆盖了Object类中的方法toString、equals和 hashCode ;
针对上面的第二点,所有对代理类的toString、equals和hashCode的方法调用,都会请求到invoke代理实现上去。所以就出现toString方法也被代理的情况了。