Java与代理——JDK原生动态代理的实现

JDK原生实现动态代理

由上一篇中的代理模式UML类图我们可以看出,代理类和被代理类需要依赖统一接口,代理类通过在接口方法的实现中增加增强逻辑并调用被代理类的实现,实现代理。这样客户端在调用的时候,代理类便可以替代被代理类,并且对外的调用接口相同。
静态代理的局限性上篇博文中已经介绍过,即接口声明的方法过多的时候,我们需要逐一进行增强添加,这样在需求变更或者接口更改的时候就会非常麻烦。
动态代理在静态代理的基础上,通过Java提供的类通过反射在运行时动态获得接口信息,并对其进行增强。我们先看一个小例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
* 定义Produce接口,提供两个行为:分别为售货和支付
* Created by gaodongyan on 2017/7/17.
*/
public interface Produce {
public void sale();
public void pay(double price);
}
/**
* Created by gaodongyan on 2017/7/17.
* 定义代理类继承自InvocationHandler,内部持有被代理对象的引用
*/
public class SuperMarket implements InvocationHandler {
private Object producer;
public SuperMarket(Object producer) {
this.producer = producer;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//proxy指代理对象,不是被代理的对象
System.out.println(proxy.getClass());
//公共增强
System.out.println("超市代理:公共增强");
//针对方法的不同,进行不同的增强
if("sale".equals(method.getName())) {
System.out.println("超市代理:超市代理了生产者,进行销售");
}
else if("pay".equals(method.getName())) {
System.out.println("超市代理:产品正在超市销售,超市赚取一定代理销售费用");
}
//调用被代理类的真实方法
method.invoke(producer, args);
return null;
}
}
/**
* Created by gaodongyan on 2017/7/17.
* 购买测试类,在该类主方法中进行动态代理的测试
*/
public class Buyer {
public static void main(String[] args) {
Produce producer = new Producer();
//InvocationHandler与代理类关联,可以理解为拦截器
InvocationHandler invocationHandler = new SuperMarket(producer);
Produce proxy = (Produce) Proxy.newProxyInstance(invocationHandler.getClass().getClassLoader(), producer.getClass().getInterfaces(), invocationHandler);
proxy.sale();
proxy.pay(20.0);
}
}
上述代码运行结果如下:
class com.sun.proxy.$Proxy0
超市代理:公共增强
超市代理:超市代理了生产者,进行销售
生产者:我售出了一个货物
class com.sun.proxy.$Proxy0
超市代理:公共增强
超市代理:产品正在超市销售,超市赚取一定代理销售费用
生产者:该货物价格为:20.0

反观上例,我们在创建动态代理的过程中。实际上是自己实现一个实现了InvocationHandler的拦截器(这里的拦截器只是一种比喻,该说法不准确),其中包含一个被代理类的实例引用。InvocationHandler接口中只有一个invoke(Object proxy, Method method, Object[] args)方法,我们便在该方法中实现对于方法的增强。并在实现增强后,调用被代理类的方法。
在使用时,我们首先创建proxy对象。该对象关联一个自定义InvocationHandler对象和一个被代理对象。我们调用代理对象的方法时,会转向调用InvocationHandler中的invoke方法。这也就实现了代码的增强(理解起来和Spring AOP类似)。

JDK原生动态代理总结

在InvocationHandler的实现中,我们可以看到,我们可以对所有方法添加公共的增强;同时也可以针对方法增加特定的增强,这有着很大的灵活性。这也是动态代理的优点之一。但是仔细观察代码实现的原理,JDK原生动态代理是通过反射获得被代理对象所实现的接口方法,并对这些接口方法进行代理和增强。虽然相比静态代理,其免去了需要逐一代理,且当被代理对象实现多个接口的时候,无需为每一个接口创建一个代理类;但是其实现仍然受到接口的约束。这也是动态代理的不足之处。
下一篇博文中,将介绍有关使用cglib代码生成库实现动态代理的原理及方法。相比于JDK原生动态代理,cglib具有更大的灵活性。