23.2 Java SDK动态代理

23.1 静态代理

我们先介绍它的用法,然后介绍实现原理,最后分析它的优点。

23.2.1 用法

在静态代理中,代理类是直接定义在代码中的,在动态代理中,代理类是动态生成的,怎么动态生成呢?我们用动态代理实现前面的例子,如代码清单23-2所示。

代码清单23-2 使用Java SDK实现动态代理示例
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
public class SimpleJDKDynamicProxyDemo {
static interface IService {
public void sayHello();
}
static class RealService implements IService {
@Overridepublic
void sayHello() {
System.out.println("hello");
}
}
static class SimpleInvocationHandler implements InvocationHandler {
private Object realObj;
public SimpleInvocationHandler(Object realObj) {
this.realObj = realObj;
}
@Overridepublic
Object invoke(Object proxy, Method method,Object[] args) throws Throwable {
System.out.println("entering " + method.getName());
Object result = method.invoke(realObj, args);
System.out.println("leaving " + method.getName());
return result;
}
}
public static void main(String[] args) {
IService realService = new RealService();
IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(), new Class<? >[] {IService.class },new SimpleInvocationHandler(realService));
proxyService.sayHello();
}
}

代码看起来更为复杂了,这有什么用呢?别着急,我们慢慢解释。IService和Real-Service的定义不变,程序的输出也没变,但代理对象proxyService的创建方式变了,它使用java.lang.reflect包中的Proxy类的静态方法newProxyInstance来创建代理对象,这个方法的声明如下:

1
2
public static Object newProxyInstance(ClassLoader loader,
Class<? >[] interfaces, InvocationHandler h)

它有三个参数,具体如下。
1)loader表示类加载器,下一章我们会单独探讨,例子使用和IService一样的类加载器。
2)interfaces表示代理类要实现的接口列表,是一个数组,元素的类型只能是接口,不能是普通的类,例子中只有一个IService。
3)h的类型为InvocationHandler,它是一个接口,也定义在java.lang.reflect包中,它只定义了一个方法invoke,对代理接口所有方法的调用都会转给该方法。

newProxyInstance的返回值类型为Object,可以强制转换为interfaces数组中的某个接口类型。这里我们强制转换为了IService类型,需要注意的是,它不能强制转换为某个类类型,比如RealService,即使它实际代理的对象类型为RealService。

SimpleInvocationHandler实现了InvocationHandler,它的构造方法接受一个参数realObj表示被代理的对象,invoke方法处理所有的接口调用,它有三个参数:
1)proxy表示代理对象本身,需要注意,它不是被代理的对象,这个参数一般用处不大。
2)method表示正在被调用的方法。
3)args表示方法的参数。

在SimpleInvocationHandler的invoke实现中,我们调用了method的invoke方法,传递了实际对象realObj作为参数,达到了调用实际对象对应方法的目的,在调用任何方法前后,我们输出了跟踪调试语句。需要注意的是,不能将proxy作为参数传递给method. invoke,比如:

1
Object result = method.invoke(proxy, args);

上面的语句会出现死循环,因为proxy表示当前代理对象,这又会调用到SimpleIn-vocationHandler的invoke方法。

23.2.2 基本原理

看了上面的介绍是不是更晕了,没关系,看下Proxy.newProxyInstance的内部就理解了。代码清单23-2中创建proxyService的代码可以用如下代码代替:

1
2
3
4
5
6
Class<? > proxyCls = Proxy.getProxyClass(IService.class.getClassLoader(),
new Class<? >[] { IService.class });
Constructor<? > ctor = proxyCls.getConstructor(
new Class<? >[] { InvocationHandler.class });
InvocationHandler handler = new SimpleInvocationHandler(realService);
IService proxyService = (IService) ctor.newInstance(handler);

分为三步:
1)通过Proxy.getProxyClass创建代理类定义,类定义会被缓存;
2)获取代理类的构造方法,构造方法有一个InvocationHandler类型的参数;
3)创建InvocationHandler对象,创建代理类对象。

Proxy.getProxyClass需要两个参数:一个是ClassLoader;另一个是接口数组。它会动态生成一个类,类名以$Proxy开头,后跟一个数字。对于上面的例子,动态生成的类定义如代码清单23-3所示,为简化起见,我们忽略了异常处理的代码。

代码清单23-3 Java SDK动态生成的代理类示例
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
final class $Proxy0 extends Proxy implementsSimpleJDKDynamicProxyDemo.IService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject) {
return((Boolean) this.h.invoke(this, m1,new Object[] {paramObject })).booleanValue();
}
public final void sayHello() {
this.h.invoke(this, m3, null);
}
public final String toString() {
return (String) this.h.invoke(this, m2, null);
}
public final int hashCode() {
return ((Integer) this.h.invoke(this, m0, null)).intValue();
}
static {
m1 = Class.forName("java.lang.Object").getMethod("equals",new Class[] {Class.forName("java.lang.Object") });
m3 = Class.forName("laoma.demo.proxy.SimpleJDKDynamicProxyDemo$IService").getMethod("sayHello", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}
}

$Proxy0的父类是Proxy,它有一个构造方法,接受一个InvocationHandler类型的参数,保存为了实例变量h, h定义在父类Proxy中,它实现了接口IService,对于每个方法,如sayHello,它调用InvocationHandler的invoke方法,对于Object中的方法,如hash-Code、equals和toString, $Proxy0同样转发给了InvocationHandler。

可以看出,这个类定义本身与被代理的对象没有关系,与InvocationHandler的具体实现也没有关系,而主要与接口数组有关,给定这个接口数组,它动态创建每个接口的实现代码,实现就是转发给InvocationHandler,与被代理对象的关系以及对它的调用由InvocationHandler的实现管理

我们是怎么知道$Proxy0的定义的呢?对于Oracle的JVM,可以配置java的一个属性得到,比如:

1
2
java  -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true  shuo.laoma.dynamic.c86.
SimpleJDKDynamicProxyDemo

以上命令会把动态生成的代理类$Proxy0保存到文件$Proxy0.class中,通过一些反编译器工具比如JD-GUI(http://jd.benow.ca/ )就可以得到源码。

理解了代理类的定义,后面的代码就比较容易理解了,就是获取构造方法,创建代理对象。

23.2.3 动态代理的优点

相比静态代理,动态代理看起来麻烦了很多,它有什么好处呢?使用动态代理,可以编写通用的代理逻辑,用于各种类型的被代理对象,而不需要为每个被代理的类型都创建一个静态代理类。看个简单的示例,如代码清单23-4所示。

代码清单23-4 通用的动态代理类示例
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
public class GeneralProxyDemo {
static interface IServiceA {
public void sayHello();
}
static class ServiceAImpl implements IServiceA {
@Override
public void sayHello() {
System.out.println("hello");
}
}
static interface IServiceB {
public void fly();
}
static class ServiceBImpl implements IServiceB {
@Override
public void fly() {
System.out.println("flying");
}
}
static class SimpleInvocationHandler implements InvocationHandler {
private Object realObj;
public SimpleInvocationHandler(Object realObj) {
this.realObj = realObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("entering " + realObj.getClass()
.getSimpleName() + "::" + method.getName());
Object result = method.invoke(realObj, args);
System.out.println("leaving " + realObj.getClass()
.getSimpleName() + "::" + method.getName());
return result;
}
}
private static <T> T getProxy(Class<T> intf, T realObj) {
return (T) Proxy.newProxyInstance(intf.getClassLoader(),
new Class<? >[] { intf }, new SimpleInvocationHandler(realObj));
}
public static void main(String[] args) throws Exception {
IServiceA a = new ServiceAImpl();
IServiceA aProxy = getProxy(IServiceA.class, a);
aProxy.sayHello();
IServiceB b = new ServiceBImpl();
IServiceB bProxy = getProxy(IServiceB.class, b);
bProxy.fly();
}
}

在这个例子中,有两个接口IServiceA和IServiceB,它们对应的实现类是Service-AImpl和ServiceBImpl,虽然它们的接口和实现不同,但利用动态代理,它们可以调用同样的方法getProxy获取代理对象,共享同样的代理逻辑SimpleInvocationHandler,即在每个方法调用前后输出一条跟踪调试语句。程序输出为:

1
2
3
4
5
6
entering ServiceAImpl::sayHello
hello
leaving ServiceAImpl::sayHello
entering ServiceBImpl::fly
flying
leaving ServiceBImpl::fly