前段时间在学习Google的Java工具库Guava, 发现其中有一个反射包, 提供了动态代理的封装功能. 深入源码一看, 发现实际上还是用了JDK提供的动态代理功能. 查阅一下网络上的相关资料, 就可以看到很多文章声称JDK的动态代理是基于反射实现的. 但仔细一想就会发现其中存在一个问题, 即动态代理是需要返回一个新的类的, 而单纯的使用反射是不能够创造出一个新的类的. 那么JDK的动态代理究竟是如何实现的呢?

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
interface A {
void sayHi(String name);
int sayHello();
}

public class TestProxy {
public static void main(String[] args) {
A proxyA = (A)Proxy.newProxyInstance(A.class.getClassLoader(), new Class[]{A.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("sayHi")) {
System.out.println("Hi, " + args[0]);
return null;
} else if (method.getName().equals("sayHello")) {
System.out.println("Hello.");
return 42;
} else {
return null;
}
}
});

proxyA.sayHi("LiZeC");
int ans = proxyA.sayHello();
System.out.println("The Answer is "+ ans);
}
}

使用JDK的动态代理需要如下几个步骤

  1. 定义需要被代理的接口
  2. 实现InvocationHandler接口, 实现具体需要被代理的逻辑
  3. 使用Proxy类创建代理类

newInstance源码分析

以下源码基于JDK11, JDK11的实现方式与JDK8略有不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
Objects.requireNonNull(h);

final Class<?> caller = System.getSecurityManager() == null
? null
: Reflection.getCallerClass();

/*
* Look up or generate the designated proxy class and its constructor.
*/
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

return newProxyInstance(caller, cons, h);
}

newProxyInstance只是做一些基本的检查,然后调用getProxyConstructor得到代理类的构造函数. 最后在newProxyInstance的另一个重载版本中通过构造函数创建对象.

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
private static Constructor<?> getProxyConstructor(Class<?> caller,
ClassLoader loader,
Class<?>... interfaces)
{
// optimization for single interface
if (interfaces.length == 1) {
Class<?> intf = interfaces[0];
if (caller != null) {
checkProxyAccess(caller, loader, intf);
}
return proxyCache.sub(intf).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
);
} else {
// interfaces cloned
final Class<?>[] intfsArray = interfaces.clone();
if (caller != null) {
checkProxyAccess(caller, loader, intfsArray);
}
final List<Class<?>> intfs = Arrays.asList(intfsArray);
return proxyCache.sub(intfs).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
);
}
}

getProxyConstructor也做了一些权限的检查并提供了一个缓存功能, 如果缓存中没有, 则会调用ProxyBuilder创建代理对象.

JDK8的逻辑实际上和JDK11的逻辑是一样的, 不过这里JDK11的代码更清晰好懂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Constructor<?> build() {
Class<?> proxyClass = defineProxyClass(module, interfaces);
final Constructor<?> cons;
try {
cons = proxyClass.getConstructor(constructorParams);
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
return cons;
}

进入到build方法之中, 可以看到通过defineProxyClass创建了代理类, 之后获取代理类的构造函数并做权限检查. 因此经过上面一系列的跳转, 我们终于进入到看到了核心方法, 也就是defineProxyClass方法


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
// 省略部分代码

/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags);
try {
Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile,
0, proxyClassFile.length,
loader, null);
reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
return pc;
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}

defineProxyClass代码很长, 做了很多设置,省略掉这些设置以后, 最核心的代码实际上就是两句

  1. 使用ProxyGenerator直接在内存中生成了一个class文件
  2. 调用defineClass方法将生成的文件加载到虚拟机中

因此实际上JDK的动态代理也是通过生成字节码并加载的方式实现的.

ProxyGenerator分析

进入ProxyGenerator可以看到, 这个类确实就是手动写Class的各种字段在内存中拼接出来了一个Class文件. 虽然代码很复杂, 设计了很多Class文件的细节, 但是我们可以直接将生成的class文件保存下来并直接反编译查看其中的逻辑.

由于ProxyGenerator类并不是public类, 所以不能直接访问, 但只要使用一些反射技巧, 就可以轻松的获得这个对象并调用generateProxyClass方法, 例如

1
2
3
4
5
6
7
8
9
Class<?> pGC = Class.forName("java.lang.reflect.ProxyGenerator");
Method generateProxyC = pGC.getDeclaredMethod("generateProxyClass", String.class, Class[].class);
generateProxyC.setAccessible(true);

byte[] classCode = (byte[])generateProxyC.invoke(null, "TestA", new Class[]{A.class});

OutputStream out = Files.newOutputStream(Path.of("TestA.class"));
out.write(classCode);
out.close();

直接使用IDEA打开保存的TestA.class文件, 可以看到如下的代码

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

public final class TestA extends Proxy implements A {
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
private static Method m0;

public TestA(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 int sayHello() throws {
try {
return (Integer)super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

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 sayHi(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 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"));
m4 = Class.forName("A").getMethod("sayHello");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("A").getMethod("sayHi", 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类), 为什么会抛出UndeclaredThrowableException等等.

不难看出, 在生成的类中确实使用了反射获得方法, 但在具体调用的时候, 还是通过字节码生成的方式直接调用的类方法而不是反射调用. 所以至少从性能的角度来说, 自己的逻辑如果不反射调用, 实际上是没有额外的反射调用开销的.

参考资料

最后更新: 2024年04月24日 15:50

版权声明:本文为原创文章,转载请注明出处

原始链接: https://lizec.top/2021/07/31/Java%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E4%B9%8B%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/