Java 实现动态代理一共有两种方法,一种是JDK Proxy,另外一种是Cglib,下面就看看JDK动态代理的实现过程,和对实现JDK代理的一个接口InvocationHandler和一个类Proxy的详解。
静态代理:就是由程序员自己创建或由特定工具自动生成源代码,在程序运行之前,代理类已经存在。(可参考设计模式中的代理模式)。
动态代理:在程序运行时,利用反射机制动态创建而成的。
要代理的目标对象(委托类)必须实现接口,代理类只能对接口进行代理,要代理的目标对象(委托类)和代理列要实现相同的接口。
下面简单举个例子:
//接口
interfa a {
void sayHello();
}
//要代理的目标对象(委托类)
class A implements a{
public void sayHello(){
System.out.println("hello,world");
}
}
要对目标对象(委托类)进行代理 就必须也要实现 a这个接口,但是代理类不是通过直接 implements去实现的,下面会有实现接口a的方式。
在JDK 实现动态代理,我们得先了解一个重要的接口InvocationHandler(实现JDK代理必须实现这个类) 和一个重要的类Proxy。
下面先说接口InvocationHandler的作用,看看官方文档对它的说明
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
翻译成中文:
InvocationHandler是由代理实例的调用处理程序实现的接口 。
每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。
也就是说我们代理类要调用目标对象(委托类)的方法都要经过InvocationHandler这个接口的invoke方法来调用。
看看InvocationHandler中唯一的一个方法invoke
public Object invoke (Object proxy,Method method ,Object [] args) throws Throwable
三个参数的含义:
Object proxy:我们要代理的目标对象
Method method: 代理对象中实现了接口中方法的实例(这里有点抽象,其实就是代理对象实现了接口中的方法,这个方法也会调用目标对象(委托类)中的同一个方法)
Object [] args: 调用目标对象(委托类)中方法的参数
返回结果:目标对象类中的方法执行后返回的结果
这个方法看着是难以理解,但通过实际操作就比较容易了解这个方法的作用了,下面会有更详细的说明。
再看看Proxy类的官方文档对它描述
Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.
翻译成中文
Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
说白了就是这个类提供了为我们创建代理类的方法,其中最常用的方法就是newProxyInstance()
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)throws IllegalArgumentException
三个参数的含义:
ClassLoader loader: 类加载器来定义代理类,用那个类加载器来加载代理对象
Class>[] interfaces: 代理类要实现的接口列表,就是代理类要实现哪些接口,这里是个数组,可以实现多个接口
InvocationHandler h:调度方法调用的调用处理函数(调用目标对象的handler)
返回结果:由指定的类加载器定义并实现指定接口的代理类的指定调用处理程序的代理实例
其实就返回我们的代理对象,下面有例子。
上面说了这么多,直接看个例子更容易理解吧。
接口
public interface A {
void sayHello();
int add(int a,int b);
}
目标对象(委托类)
public class AImpl implements A {
@Override
public void sayHello() {
System.out.println("hello , world");
}
@Override
public int add(int a, int b) {
System.out.println("add()方法的执行结果:" + (a + b));
return a + b;
}
}
动态代理类
public class AProxy implements InvocationHandler {
//我们要代理的目标对象(委托类)
private AImpl A;
//通过构造方法将我们的目标对象传进去
public AProxy(AImpl a) {
A = a;
}
//处理代理实例上的方法调用并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在我们真正调用目标对象的方法之前和之后我们可以添加一些自己的操作,例如用户认证,日志记录等
//例如在我们方法调用之前先进行用户验证,调用方法之后再用日志记录,下面用伪代码来说明
System.out.println("对用户进行验证....验证通过,执行方法");
//A是我们要进行代理的目标对象,args是执行目标对象中方法所需的参数
Object invoke = method.invoke(A, args);
System.out.println("目标对象方法执行后返回的结果:"+invoke);
System.out.println("日志:记录用户的操作");
System.out.println();
//返回的是目标对象方法执行后的结果
return invoke;
}
}
客户类
newProxyInstance()方法返回的动态代理类是JVM运行时动态生成的
public class ProxyTestDemo {
public static void main(String[] args) {
//也可以用多态 A a = new AImpl()
AImpl Aimpl = new AImpl();
//将我们要代理的目标对象传进去
AProxy aProxy = new AProxy(Aimpl);
//创建目标对象的代理类
A a = (A) Proxy.newProxyInstance(A.class.getClassLoader(), Aimpl.getClass().getInterfaces(), aProxy);
a.sayHello();
a.add(1, 2);
}
}
运行结果
从运行结果可以看到我们在调用目标对象中的方法时,可以前后添加我们的一些操作,其实这跟Spring Aop中Adivce的差不多 ,上面添加的操作类似于前置通知,后置通知。
上面我们还说过InvacationHandler接口中invoke()方法的返回结果是我们目标对象方法执行后的结果,下面证明一下。
直接在动态代理类中添加下面的一句输出,看看在控制台中打印出看看是不是我们目标对象方法的运行结果。
第一个方法没有返回值,所以返回null,第二个方法有返回值,所以直接返回目标对象方法的执行结果的返回值,通过上面的输出可以证明Method的invoke方法是返回目标对象方法的执行结果。