0%

Java动态代理

JDK动态代理

Java中可以使用 Proxy.newProxyInstance​ 创建动态代理,动态代理可以拦截对被代理对象的方法调用,并为其附加额外功能。这种似乎一般称为JDK动态代理。

该方法的描述如下:

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
public static Object newProxyInstance​(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
Returns a proxy instance for the specified interfaces that dispatches method invocations to the specified invocation handler.
IllegalArgumentException will be thrown if any of the following restrictions is violated:

All of Class objects in the given interfaces array must represent interfaces, not classes or primitive types.
No two elements in the interfaces array may refer to identical Class objects.
All of the interface types must be visible by name through the specified class loader. In other words, for class loader cl and every interface i, the following expression must be true:
Class.forName(i.getName(), false, cl) == i

All of the types referenced by all public method signatures of the specified interfaces and those inherited by their superinterfaces must be visible by name through the specified class loader.
All non-public interfaces must be in the same package and module, defined by the specified class loader and the module of the non-public interfaces can access all of the interface types; otherwise, it would not be possible for the proxy class to implement all of the interfaces, regardless of what package it is defined in.
For any set of member methods of the specified interfaces that have the same signature:
If the return type of any of the methods is a primitive type or void, then all of the methods must have that same return type.
Otherwise, one of the methods must have a return type that is assignable to all of the return types of the rest of the methods.
The resulting proxy class must not exceed any limits imposed on classes by the virtual machine. For example, the VM may limit the number of interfaces that a class may implement to 65535; in that case, the size of the interfaces array must not exceed 65535.
Note that the order of the specified proxy interfaces is significant: two requests for a proxy class with the same combination of interfaces but in a different order will result in two distinct proxy classes.

Parameters:
loader - the class loader to define the proxy class
interfaces - the list of interfaces for the proxy class to implement
h - the invocation handler to dispatch method invocations to
Returns:
a proxy instance with the specified invocation handler of a proxy class that is defined by the specified class loader and that implements the specified interfaces
Throws:
IllegalArgumentException - if any of the restrictions on the parameters are violated
SecurityException - if a security manager, s, is present and any of the following conditions is met:
the given loader is null and the caller's class loader is not null and the invocation of s.checkPermission with RuntimePermission("getClassLoader") permission denies access;
for each proxy interface, intf, the caller's class loader is not the same as or an ancestor of the class loader for intf and invocation of s.checkPackageAccess() denies access to intf;
any of the given proxy interfaces is non-public and the caller class is not in the same runtime package as the non-public interface and the invocation of s.checkPermission with ReflectPermission("newProxyInPackage.{package name}") permission denies access.
NullPointerException - if the interfaces array argument or any of its elements are null, or if the invocation handler, h, is null

它的基本原理是按照接口的差异为各种类型的被代理对象动态生成一个新类型,称为代理类。代理类其实就是一个实现了被代理对象所需接口的新类型,它会在生成的各种接口方法中调用 InvocationHandlerinvoke 方法,所以一般情况下需要在 InvocationHandler 中为被代理对象调用相应的方法,以实现其原本的功能。

一个实例

下面的例子中声明了两个接口,ISayISpeakHello 类实现了 ISayISpeak 两个接口。

MyInvocationHandler 接受一个被代理对象 proxiedObject 保存起来,它会在 invoke 中输出它自己和proxy参数的地址,以及 method 方法的描述,并在最后调用了被代理实例的相应 method

main 方法中的 myInvocationHandler.invoke 很容易理解,它就是一个正常的方法调用。关键在于,后面的 proxyInstance.say 其实完全等价于前面的 myInvocationHandler.invoke 调用。

接下来给出源码,后面展示输出并进行进一步的分析。

源码

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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxyTest3 {
interface ISay {
void say(String what);
}

interface ISpeak {
void speak(String what);
}

static class Hello implements ISay, ISpeak {
@Override
public void say(String what) {
System.out.printf("Hello.say: %s%n", what);
}

@Override
public void speak(String what) {

}
}

public static class MyInvocationHandler implements InvocationHandler {
private final Object proxiedObject;
public MyInvocationHandler(Object proxiedObject) {
this.proxiedObject = proxiedObject;
}

@Override
public Object invoke(Object proxyInstance, Method method, Object[] args) throws Throwable {
System.out.printf("invocationHandlerHashCode: {%s}%n", System.identityHashCode(this));
System.out.printf("method: {%s}%n", method.toString());
System.out.printf("proxyInstanceHashCode: {%s}%n", System.identityHashCode(proxyInstance));
System.out.printf("proxyInstanceClass: {%s}%n", proxyInstance.getClass());
return method.invoke(proxiedObject, args);
}
}

public static void main(String[] margs) throws Throwable {
Hello helloInstance = new Hello();
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(helloInstance);

ISay proxyInstance = (ISay) Proxy.newProxyInstance(
Hello.class.getClassLoader(),
Hello.class.getInterfaces(),
myInvocationHandler);

myInvocationHandler.invoke(proxyInstance, ISay.class.getDeclaredMethod("say", String.class), new Object[]{"we can reach corner in the world"});

proxyInstance.say("Across the Great Wall");

Hello proxyInstance2HelloInstance = (Hello) proxyInstance;
}
}

运行输出

程序输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
invocationHandlerHashCode: {789451787}
method: {public abstract void DynamicProxyTest3$ISay.say(java.lang.String)}
proxyInstanceHashCode: {1950409828}
proxyInstanceClass: {class $Proxy0}
Hello.say: we can reach corner in the world

invocationHandlerHashCode: {789451787}
method: {public abstract void DynamicProxyTest3$ISay.say(java.lang.String)}
proxyInstanceHashCode: {1950409828}
proxyInstanceClass: {class $Proxy0}
Hello.say: Across the Great Wall

Exception in thread "main" java.lang.ClassCastException: $Proxy0 cannot be cast to DynamicProxyTest3$Hello
at DynamicProxyTest3.main(DynamicProxyTest3.java:55)

Process finished with exit code 1

可以看到动态代理对象实例 proxyInstance 的类型是 $Proxy0,这是程序运行时动态生成的一个类型,通过执行Java虚拟机参数 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true 就可以输出这个动态生成的类的class字节码,反编译可以得到下面的内容。

可以看到,该代理类实现了 ISayISpeak 接口,它将接口中的每一个方法都写成了 super.h.invoke(this, methodObject, new Object[]{var1, var2, ...}) 的形式。

super.h 其实就是创建动态代理时传入的 InvocationHandler 对象,在这个例子里就是 myInvocationHandler。第一个参数 this 就是该代理对象本身,对应于本例的 proxyInstance,后面两个参数分别是 methodmethod 所需参数。

由此可见 main 方法中的 proxyInstance.say 其实完全等价于前面的 myInvocationHandler.invoke 调用。

这就是动态代理的基本原理,实际应用时可以在 InvocationHandler 中附加自己所需的操作来增强被代理类的功能,比如记录日志。

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import DynamicProxyTest3.ISay;
import DynamicProxyTest3.ISpeak;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0 extends Proxy implements ISay, ISpeak {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
private static Method m0;

public $Proxy0(InvocationHandler var1) throws {
super(var1);
}

public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}

public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

public final void say(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}

public final void speak(String var1) throws {
try {
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}

public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("DynamicProxyTest3$ISay").getMethod("say", Class.forName("java.lang.String"));
m4 = Class.forName("DynamicProxyTest3$ISpeak").getMethod("speak", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}

JDK动态代理的局限性

该实例的最后一句调用试图将动态代理类实例转换成被代理的类型,但并没有成功,因为代理类只是实现了被代理类的接口,它本身并不是被代理的类型。

由于这种 Proxy 动态代理是基于接口的,被代理类需要实现相应的接口才能进行代理,但是实际使用中有可能难以或者懒得对被代理类进行改造,这种情况下可以动态代理吗?答案是可以!不过JDK动态代理显然是不行的,但 cglib 可以实现,它不依赖接口,生成的动态代理对象还可以转换成被代理的类型,具体原理我还没看。