AOP
1 概述
在对静态代理的讨论中,可以发现,如果方法比较多的情况下,为每种方法做代理,是一件重复和繁琐的工作。需要有一种机制能够截获方法调用,从而集中处理。这就是将探讨的动态代理。
在Java中,动态代理是指代理类的生成发生在程序运行时期,根据被代理类动态生成代理类的字节码文件(class文件),并且装载到运行环境中,像其他的类一样使用,该字节码文件的生存期随着运行环境的关闭而消失。
JDK1.3以及之上的版本提供了动态代理的机制,它和反射机制结合在一起。下面将以一个例子说明JDK的动态代理。
2 JDK动态代理
2.1 JDK提供的接口和类
-
InvocationHandler接口
该接口的定义如下:
它定义了唯一的方法invoke,该方法的参数为代理对象proxy,截获的方法对象method,和方法调用的参数,返回方法执行的结果。
开发者需要实现该接口,在invoke方法中添加对截获的方法的代理操作,并调用被代理对象的方法。
-
Proxy类
该类是生成代理类的帮助类,它的几个主要方法如下:
public Static Class getProxyClass (ClassLoader loader, Class[] interfaces):根据指定的类加载器和接口获得一个代理类,其中,loader是类加载器,interfaces是被代理类所拥有的接口。
public Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):根据指定的类加载器、接口以及截获器,返回代理类的一个实例对象。
2.2 Comand接口和ConnectCommand类
我们仍然以静态代理一文中的场景中的Command接口和ConnectCommand类为例说明。Command接口如下:
ConnectCommand类如下,仅作为演示使用。
2.3基于JDK的动态代理的实现
截获器InvocationHandler的实现如下:
ConnectCommandHandler在被代理对象original的方法执行前后,可以做一些代理的操作,这里仅仅输出信息作为演示。
创建代理的类JDKProxyCreator的实现如下:
测试类如下:
测试结果如下:
Without proxying:
Excuting command...
With interface proxying(jdk-dynamic):
Before excuting command...
Excuting command...
After excuting command...
3 分析总结
分析JDK提供的帮助类Proxy,可以发现,它只支持实现接口方式的代理,不支持继承超类方式的代理,这就意味着,被代理的类必须要有接口,并且需要拦截的方法必须都在接口中进行声明。在本例中,通过截获帮助类Proxy生成的代理类的字节码文件,反编译之后,发现,其类的声明如下:
public final class proxy0 extends Proxy implements Command
其中,proxy0就是动态生成的代理类的名字,在该类的方法中,都会将调用委托到拦截器的invoker方法。
因为JDK提供的动态代理仅支持接口方式的代理,那么如果被代理的类没有实现任何接口,如何处理呢?后续将探讨基于CGLib的动态代理,它同时也支持继承超类的方式的代理。