Java基础 - JDK动态代理

概述

Java 中代理分为两大类,一类是静态代理,另一类是动态代理。静态代理是针对需要被代理的类在编译之前就已经写好了对应的代理类,也就是说代理关系在编译之前就确立了。动态代理是针对目标类在程序运行期间自动生成的代理类,细分为有接口的代理类和无接口的代理类。JDK动态代理支持目标类有接口的情况,目标类没有接口无法为其生成代理类,能够为没有接口生成代理类的工具如 CGLIB 等。本篇文章将对JDK动态代理实现原理进行介绍。

示例

接口

1
2
3
4
5
6
7
8
public interface IPrintf {
/**
* 打印信息
*
* @param message 信息
*/
void print(String message);
}

实现类

1
2
3
4
5
6
7
public class PrintfImpl implements IPrintf {

@Override
public void print(String message) {
System.out.println("print: " + message);
}
}

代理对象

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
public class Client {
/**
* 实例化对象
*/
private static final IPrintf PRINTF = new PrintfImpl();

/**
* JDK 生成代理对象
*
* @return $Proxy0
*/
public static IPrintf getProxy() {

// 返回代理对象
return (IPrintf) Proxy.newProxyInstance(
// 类加载器,在程序运行时将生成的代理类加载到JVM中
PRINTF.getClass().getClassLoader(),
// 被代理类的所有接口信息,用来确定生成的代理类可以具有哪些方法
PRINTF.getClass().getInterfaces(),
// 调用处理器,每个代理对象都具有一个关联的调用处理器,用于指定动态生成的代理类需要完成的具体操作。
// 该接口中有一个 invoke 方法,代理对象调用任何目标接口的方法时都会调用该 invoke 方法,该方法中会通过反射调用目标方法。
new InvocationHandler() {
/**
*
* @param proxy 代理对象
* @param method 代理对象当前调用的方法
* @param args 方法参数
* @return 方法执行的结果(无返回值则为 null)
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置逻辑
System.out.println("before action ... ");
// 将方法派发给目标方法
Object result = method.invoke(PRINTF, args);
// 后置逻辑
System.out.println("after action ... ");
return result;
}
}
);
}
}

测试

1
2
3
4
5
6
7
8
public class ProxyTest {

@Test
public void test() {
IPrintf proxy = Client.getProxy();
proxy.print("hello world!");
}
}

打印结果

代理类

通过阿里开源 Java 应用诊断工具 Arthas 反编译代理类,结果如下:

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
89
90
91
92
93
94
95
96
97
/*
* Decompiled with CFR.
*/
package com.sun.proxy;

import com.code.proxy.IPrintf;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy4
extends Proxy
implements IPrintf {
// 静态属性,每个属性对应接口中的一个方法
private static Method m1;
private static Method m2;
private static Method m0;
private static Method m3;

// 构造方法,入参类型为 InvocationHandler
public $Proxy4(InvocationHandler invocationHandler) {
// 调用父类 Proxy 的构造方法
super(invocationHandler);
}

// 静态代码块中通过反射初始化Method属性
static {
try {
// Object 中的三大方法
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
// 目标类的接口中的方法
m3 = Class.forName("com.code.proxy.IPrintf").getMethod("print", Class.forName("java.lang.String"));
return;
}
// 异常处理
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}

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

public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

// 目标类的打印方法
public final void print(String string) {
try {
// this.h 是 Proxy 中的属性,即调用 Proxy.newProxyInstance 方法传入的 InvocationHandler 对象
this.h.invoke(this, m3, new Object[]{string});
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}

通过上面反编译后的代理类代码不难看出,JDK 动态代理实现具有以下特点:

  1. 生成的代理类继承了 Proxy 类且实现了 目标类的接口,有参构造方法的参数类型是 InvocationHandler ,反射创建代理对象执行的就是该构造方法。
  2. 代理类通过反射为目标接口(接口列表)中的每个方法都映射一个 Method 对象。
  3. 代理类对接口中方法的实现逻辑都是通过 InvocationHandler.invoke 方法派发执行的,代理对象调用任何目标接口的方法时都会调用这个invoke方法,该方法中进行目标类的目标方法的调用,即每个方法执行逻辑都由第 2 步中的 Method 对象执行。

关于 JDK 动态代理使用就介绍完毕了,下面我们对底层实现原理进行说明。实现原理中的部分描述信息会引用到上述代码片段。

JDK 动态代理

Java 中需要在运行期动态的生成一个类并创建其对象,一般需要使用字节码技术和反射机制。JDK 动态代理通过java.lang.reflect.Proxy提供了一种原生的动态代理模式,其底层通过对字节码的操作反射的使用组装代理类,如前文中的 $Proxy4,最后通过反射创建代理对象。

JDK通过调用静态方法 Proxy.newProxyInstance() 创建动态代理,该方法需要三个参数:

  1. 类加载器
    通常可以从已经被加载的对象中获取其类加载器。
  2. 接口列表
    预期代理实现的接口列表。
  3. InvocationHandler 接口的实现
    作为动态代理对象的调用处理器,即动态代理可以将所有调用派发到该调用处理器。因此,通常会向调用处理器的构造器中传入一个目标对象的引用,从而使得调用处理器在执行中介任务时可以将请求转发。

介绍完代理相关的概念和使用方式后,下面我们对 JDK 动态代理实现原理进行说明。

源码分析

Proxy

属性

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
+--- java.lang.reflect.Proxy
/**
* 构造方法参数类型,就是 InvocationHandler
*/
private static final Class<?>[] constructorParams = {InvocationHandler.class};

/**
* 代理类的缓存
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

/**
* 代理对象调用的处理器
*
* @serial
*/
protected InvocationHandler h;

/**
* Prohibits instantiation.
*/
private Proxy() {
}

/**
* 供生成的动态代理类调用,也就是 Proxy 的子类。
*
* @param h 用于代理对象的调用处理器
* @throws NullPointerException
*/
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}

// 省略其它代码...
}

Proxy 中有 3 个核心属性,下面简单介绍:

  1. constructorParams
    Proxy 中的有参构造器的参数,是个固定值即调用处理器 InvocationHandler,生成的代理类都会调用 Proxy 这个父类的构造方法。
  2. proxyClassCache
    缓存生成的代理类,用于提高效率。需要注意的是,KeyFactory 和 ProxyClassFactory 都是 Proxy 的内部类,前者用于返回接口对应的弱引用,后者根据指定的类加载器和接口列表生成代理类。
  3. h
    调用处理器,该处理器会将代理对象的方法调用派发给目标方法。

内部类 KeyFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private static final class KeyFactory
implements BiFunction<ClassLoader, Class<?>[], Object> {

/**
* 返回接口对应的弱引用信息。Key1、Key2 以及 KeyX 都持有 WeakReference
*
* @param classLoader
* @param interfaces
* @return
*/
@Override
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
switch (interfaces.length) {
case 1:
return new Key1(interfaces[0]); // the most frequent
case 2:
return new Key2(interfaces[0], interfaces[1]);
case 0:
return key0;
default:
return new KeyX(interfaces);
}
}
}

KeyFactory 就一个工作,返回接口对应的弱引用信息,KeyN 继承了 WeakReference 类。

内部类 ProxyClassFactory

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
89
90
91
92
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {

// 代理类名称前缀,具体名称为: $Proxy + Num
private static final String proxyClassNamePrefix = "$Proxy";

// 生成代理名称的序号,是自增原子类
private static final AtomicLong nextUniqueNumber = new AtomicLong();

/**
* 生成代理类的逻辑
*
* @param loader 类加载器
* @param interfaces 接口集合
* @return 代理类
*/
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
// 遍历接口集合
for (Class<?> intf : interfaces) {

// 1 验证类加载器是否将当前接口名称解析为相同的类对象
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}

// 2 判断是否是接口
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}

// 3 验证接口是否重复加载
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}

// 4 代理所在包的名称
String proxyPkg = null;
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;


// 5 记录非public类型的接口,如果是非public类型的接口,则会将代理类定义在该对应的包中。当且仅当所有非public类型的接口都在一个包中才行,否则不合法
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}

// 6 如果接口是public类型的,则使用固定的包名: com.sun.proxy
if (proxyPkg == null) {
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}

// 7 组装代理类名称,格式:proxyPkg + $Proxy + Num
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;

// 8 根据代理类名和接口列表使用 ProxyGenerator 生成指定的代理类,可能返回null (配置了虚拟机参数,将代理类字节信息输出到文件)
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

try {

// 9 调用native方法,返回代理类
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);

} catch (ClassFormatError e) {

// 排除代理类生成代码中的bug,提供给代理类创建的参数存在其他问题(例如超出了虚拟机限制)。
throw new IllegalArgumentException(e.toString());
}
}
}

ProxyClassFactory 完成生成字节码的操作,是生成代理类的完整流程,具体工作如下:

  1. 根据目标类的接口类型确定生成代理类全路径名
  2. 执行 ProxyGenerator.generateProxyClass 方法,根据代理类名和接口生成代理类字节码数组或文件形式
  3. 调用 native 方法将代理类字节码数据转化为代理类 Class

ProxyGenerator

ProxyClassFactory.apply 通过调用 ProxyGenerator.generateProxyClass 方法组装代理类,对接口的 Class 对象、Method 对象进行拆解、封装进而生成字节码层面的方法、构造方法以及静态代码块。

属性

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
+--- ProxyGenerator

// JVM参数,是否将生成的代理类保存到文件
private static final boolean saveGeneratedFiles = (Boolean) AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));

// Object 三大方法
private static Method hashCodeMethod;
private static Method equalsMethod;
private static Method toStringMethod;

// 代理类全路径名
private String className;
// 接口数组
private Class<?>[] interfaces;
private int accessFlags;
/**
* ProxyGenerator 中的常量池
*/
private ProxyGenerator.ConstantPool cp = new ProxyGenerator.ConstantPool();
/**
* 记录方法标识,用于生成代理类中的方法的属性。
*/
private List<ProxyGenerator.FieldInfo> fields = new ArrayList();
/**
* 生成的方法信息集合,包括构造方法、静态代码块。
*/
private List<ProxyGenerator.MethodInfo> methods = new ArrayList();
/**
* 方法签名到方法代理对象的映射
*/
private Map<String, List<ProxyGenerator.ProxyMethod>> proxyMethods = new HashMap();
private int proxyMethodCount = 0;

static {
// 反射获取三大方法对象
try {
hashCodeMethod = Object.class.getMethod("hashCode");
equalsMethod = Object.class.getMethod("equals", Object.class);
toStringMethod = Object.class.getMethod("toString");
} catch (NoSuchMethodException var1) {
throw new NoSuchMethodError(var1.getMessage());
}
}

/**
* 构造方法
*
* @param var1 代理类名称
* @param var2 目标类接口集合
* @param var3
*/
private ProxyGenerator(String var1, Class<?>[] var2, int var3) {
this.className = var1;
this.interfaces = var2;
this.accessFlags = var3;
}

JDK 动态代理涉及到的基础类先介绍到这里,下面我们从 Proxy.newProxyInstance 入口出发,根据调用链逐步分析源代码。

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
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
+--- Proxy

/**
* 创建代理对象。
*
* @param loader 代理类的加载器
* @param interfaces 目标类的接口列表
* @param h 代理对象方法调用都会分派给该调用处理器
* @return 代理对象
* @throws IllegalArgumentException
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {

// 调用处理器是必传参数
Objects.requireNonNull(h);

// 接口列表
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}

// 1. 查找或生成指定的代理类。
Class<?> cl = getProxyClass0(loader, intfs);

try {
// 对生成的代理类进行安全检查
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}

// 2. 获取代理类的指定构造方法,即参数类型为 InvocationHandler 的构造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;

// 3. 保证代理类的构造方法 cons 具有访问权限,便于后续反射创建代理对象
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}

// 4. 反射创建代理对象,注意参数为 InvocationHandler
return cons.newInstance(new Object[]{h});

} catch (IllegalAccessException | InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}

Proxy.newProxyInstance 方法是生成代理对象的入口,下面对该方法的逻辑进行简单说明:

  1. 调用 getProxyClass0 方法根据指定的类加载器和接口列表获取代理类。这一步是整个代理逻辑的核心实现。
  2. 反射获取参数为 InvocationHandler的代理类的构造方法,并保证该构造方法是可访问的。
  3. 通过 newInstance 方法反射创建代理对象,参数类型为 InvocationHandler

第 2、3 步都很容易理解,下面我们重点来分析获取代理类的 getProxyClass0 方法。

getProxyClass0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+--- Proxy

/**
* 生成代理类。在调用此方法之前,必须调用checkProxyAccess方法来执行权限检查。
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}

/**
* 获取代理类:
* 1. 如果代理类存在则返回缓存的副本。
* 2. 不存在,则通过 ProxyClassFactory 创建代理类
*/
return proxyClassCache.get(loader, interfaces);
}

前文中有提到代理类缓存属性 WeakCache proxyClassCache ,它的主要作用就是先查找对应的代理类缓存,没有的话就通过 java.lang.reflect.Proxy.ProxyClassFactory#apply 方法创建代理类,该方法在前文中已经详细说明。下面我们对 ProxyGenerator.generateProxyClass 根据代理类名和接口生成代理类字节码数组或文件形式 这一步骤进行说明。

generateProxyClass

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
+--- ProxyGenerator

/**
* 生成代理类-字节码形式
*
* @param var0 代理类名称
* @param var1 目标类接口
* @param var2
* @return
*/
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {

ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
// 生成目标类的接口信息的字节码
final byte[] var4 = var3.generateClassFile();

// 添加JVM 参数 sun.misc.ProxyGenerator.saveGeneratedFiles ,则保存到文件
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
// 创建目录
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);

var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}

// 保存到文件
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}

return var4;
}

generateProxyClass 方法用于根据代理类名称和接口列表生成预期的代理类的字节码(数组)信息,最后通过 JDK 的本地方法转化为具体的代理类。该方法支持通过配置JVM参数将目标代理类输出到文件中。

在分析 ProxyGenerator.generateClassFile 方法之前,我们先对涉及的核心类和方法进行简单说明。注意该方法整个流程比较复杂,本文只对整体逻辑进行说明,具体细节可以参考源代码。

ProxyMethod

方法代理类,用于拆解、封装 Method 的信息,作为后续方法字节码生成的数据来源。

属性

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
+--- ProxyGenerator
private class ProxyMethod {
// 方法名
public String methodName;
// 参数类型
public Class<?>[] parameterTypes;
// 返回值类型
public Class<?> returnType;
// 异常类型
public Class<?>[] exceptionTypes;
// 方法所在接口
public Class<?> fromClass;
// 当前方法对应的序列号,如 m0
public String methodFieldName;

private ProxyMethod(String var2, Class<?>[] var3, Class<?> var4, Class<?>[] var5, Class<?> var6) {
this.methodName = var2;
this.parameterTypes = var3;
this.returnType = var4;
this.exceptionTypes = var5;
this.fromClass = var6;
this.methodFieldName = "m" + ProxyGenerator.this.proxyMethodCount++;
}

// 省略其它代码
}

generateMethod

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
89
90
91
+--- ProxyMethod
private ProxyGenerator.MethodInfo generateMethod() throws IOException {
// 方法签名 如:toString 方法的描述信息 Ljava/lang/reflect/Method; void print(String message) 描述信息 (Ljava/lang/String;)V
String var1 = ProxyGenerator.getMethodDescriptor(this.parameterTypes, this.returnType);

// 创建 MethodInfo 对象,同时会初始化 ByteArrayOutputStream 对象

// 1. 方法名 + 方法签名
ProxyGenerator.MethodInfo var2 = ProxyGenerator.this.new MethodInfo(this.methodName, var1, 17);

// 2. 方法参数
int[] var3 = new int[this.parameterTypes.length];
int var4 = 1;
for (int var5 = 0; var5 < var3.length; ++var5) {
var3[var5] = var4;
var4 += ProxyGenerator.getWordsPerType(this.parameterTypes[var5]);
}
byte var7 = 0;

// 通过字节输出流 ByteArrayOutputStream 写入方法的二进制信息,即 MethodInfo 中的输出流中
DataOutputStream var9 = new DataOutputStream(var2.code);
ProxyGenerator.this.code_aload(0, var9);

// 3 写入接口方法的 InvocationHandler.invoke 处理逻辑
var9.writeByte(180);
var9.writeShort(ProxyGenerator.this.cp.getFieldRef("java/lang/reflect/Proxy", "h", "Ljava/lang/reflect/InvocationHandler;"));
ProxyGenerator.this.code_aload(0, var9);
var9.writeByte(178);
var9.writeShort(ProxyGenerator.this.cp.getFieldRef(ProxyGenerator.dotToSlash(ProxyGenerator.this.className), this.methodFieldName, "Ljava/lang/reflect/Method;"));
if (this.parameterTypes.length > 0) {
ProxyGenerator.this.code_ipush(this.parameterTypes.length, var9);
var9.writeByte(189);
var9.writeShort(ProxyGenerator.this.cp.getClass("java/lang/Object"));
for (int var10 = 0; var10 < this.parameterTypes.length; ++var10) {
var9.writeByte(89);
ProxyGenerator.this.code_ipush(var10, var9);
this.codeWrapArgument(this.parameterTypes[var10], var3[var10], var9);
var9.writeByte(83);
}
} else {
var9.writeByte(1);
}
var9.writeByte(185);
var9.writeShort(ProxyGenerator.this.cp.getInterfaceMethodRef("java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"));
var9.writeByte(4);
var9.writeByte(0);

// 4 返回类型
if (this.returnType == Void.TYPE) {
var9.writeByte(87);
var9.writeByte(177);
} else {
this.codeUnwrapReturnValue(this.returnType, var9);
}

// 5 写入方法处理异常字节信息
short var6;
short var8 = var6 = (short) var2.code.size();
List var13 = ProxyGenerator.computeUniqueCatchList(this.exceptionTypes);
if (var13.size() > 0) {
Iterator var11 = var13.iterator();
while (var11.hasNext()) {
Class var12 = (Class) var11.next();
var2.exceptionTable.add(new ProxyGenerator.ExceptionTableEntry(var7, var8, var6, ProxyGenerator.this.cp.getClass(ProxyGenerator.dotToSlash(var12.getName()))));
}
var9.writeByte(191);
var6 = (short) var2.code.size();
var2.exceptionTable.add(new ProxyGenerator.ExceptionTableEntry(var7, var8, var6, ProxyGenerator.this.cp.getClass("java/lang/Throwable")));
ProxyGenerator.this.code_astore(var4, var9);
var9.writeByte(187);
var9.writeShort(ProxyGenerator.this.cp.getClass("java/lang/reflect/UndeclaredThrowableException"));
var9.writeByte(89);
ProxyGenerator.this.code_aload(var4, var9);
var9.writeByte(183);
var9.writeShort(ProxyGenerator.this.cp.getMethodRef("java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V"));
var9.writeByte(191);
}

if (var2.code.size() > 65535) {
throw new IllegalArgumentException("code size limit exceeded");
} else {
var2.maxStack = 10;
var2.maxLocals = (short) (var4 + 1);
var2.declaredExceptions = new short[this.exceptionTypes.length];

for (int var14 = 0; var14 < this.exceptionTypes.length; ++var14) {
var2.declaredExceptions[var14] = ProxyGenerator.this.cp.getClass(ProxyGenerator.dotToSlash(this.exceptionTypes[var14].getName()));
}
return var2;
}
}

generateMethod 方法根据 ProxyMethod 信息,按照方法的构造组装代表字节码信息的 MethodInfo,最终将该信息写入到输出流中。

MethodInfo

ProxyMethod 对象经过解析后会组装成 MethodInfo 对象,该对象封装了代理类中方法的字节码信息。

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
+--- ProxyGenerator
private class MethodInfo {
public int accessFlags;
// 方法名
public String name;
// 方法签名
public String descriptor;
public short maxStack;
public short maxLocals;
// 输出流,方法字节码会写入到该流中
public ByteArrayOutputStream code = new ByteArrayOutputStream();
public List<ProxyGenerator.ExceptionTableEntry> exceptionTable = new ArrayList();
public short[] declaredExceptions;

public MethodInfo(String var2, String var3, int var4) {
this.name = var2;
this.descriptor = var3;
this.accessFlags = var4;
ProxyGenerator.this.cp.getUtf8(var2);
ProxyGenerator.this.cp.getUtf8(var3);
ProxyGenerator.this.cp.getUtf8("Code");
ProxyGenerator.this.cp.getUtf8("Exceptions");
}

// 省略写入输出流逻辑
}

FieldInfo

FieldInfo 用于封装方法在代理类中的静态属性信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
+--- ProxyGenerator
private class FieldInfo {
public int accessFlags;
// m + N,方法在代理类中的字段名
public String name;
// 固定值 Ljava/lang/reflect/Method;
public String descriptor;

public FieldInfo(String var2, String var3, int var4) {
this.name = var2;
this.descriptor = var3;
this.accessFlags = var4;
ProxyGenerator.this.cp.getUtf8(var2);
ProxyGenerator.this.cp.getUtf8(var3);
}

// 写入输出流
public void write(DataOutputStream var1) throws IOException {
var1.writeShort(this.accessFlags);
var1.writeShort(ProxyGenerator.this.cp.getUtf8(this.name));
var1.writeShort(ProxyGenerator.this.cp.getUtf8(this.descriptor));
var1.writeShort(0);
}
}

介绍完必要的前置概念和类信息后,我们回到 ProxyGenerator.generateClassFile 方法,继续接着流程往下分析。

generateClassFile

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
+--- ProxyGenerator

private byte[] generateClassFile() {
// 1. 将 Object 中的三大方法对象Method拆解,组装成 ProxyMethod 对象
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);

// 2. 将目标类的接口中的方法对象Method拆解,组装成 ProxyMethod 对象
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
// 2.1 遍历接口
for (var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
// 2.3 获取接口中的方法列表
Method[] var5 = var4.getMethods();
int var6 = var5.length;
// 2.4 遍历当前接口中的方法
for (int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
// 2.5 对接口中的方法拆解、组装成 ProxyMethod 对象,然后加入缓存 proxyMethods 中
this.addProxyMethod(var8, var4);
}
}

// 获取缓存的方法代理对象 ProxyMethod 集合
Iterator var11 = this.proxyMethods.values().iterator();
// 校验相同方法签名的返回类型
List var12;
while (var11.hasNext()) {
var12 = (List) var11.next();
checkReturnTypes(var12);
}
Iterator var15;
try {

// 3. 生成构造方法字节码信息并加入到 methods 缓存起来
this.methods.add(this.generateConstructor());

// 4. 遍历方法代理对象列表,生成方法字节码信息并缓存到 methods 中
var11 = this.proxyMethods.values().iterator();
while (var11.hasNext()) {
var12 = (List) var11.next();
var15 = var12.iterator();
while (var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod) var15.next();

// 4.1 生成方法的 FieldInfo 并加入到 fields 集合中,即代理类中方法Method的字段属性
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));

// 4.2 生成方法的 MethodInfo 并加入到 methods 集合中,注意 MethodInfo 中的 ByteArrayOutputStream
this.methods.add(var16.generateMethod());
}
}

// 5. 生成静态代码块字节码信息(根据 proxyMethods 中的信息)
this.methods.add(this.generateStaticInitializer());

} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}

// 6. 方法数量限制
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {

// 7. 代理类字节码组装

// 7.1. 处理代理类 和 Proxy 的全路径类,并入放入常量池 cp 中
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");

// 7.2. 处理代理类的接口,并放入常量池 cp 中
var1 = this.interfaces;
var2 = var1.length;
for (var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
this.cp.setReadOnly();

// 7.3. 字节类型数据的输出流,内存操作流
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);

try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);

// 7.3.1 写入代理类名称信息
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));

// 7.3.2 写入Proxy名称信息
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));

// 7.3.4 写入接口列表
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
// 遍历接口列表
for (int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
// 写入接口信息
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}

/// 7.3.5 写入字段信息,即接口方法的标识,如 m1、m2
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while (var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo) var15.next();
var20.write(var14);
}

// 7.3.6 写入方法,包括构造方法、静态代码块
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while (var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo) var15.next();
var21.write(var14);
}
var14.writeShort(0);

// 4 返回字节数组信息
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}

generateClassFile 方法逻辑还是比较复杂的,下面对主要流程进行说明:

  1. 将 Object 中的三大方法对象解析、组装成 ProxyMethod 对象。
  2. 将目标类的接口中的方法对象解析、组装成 ProxyMethod 对象。
  3. 生成构造方法字节码信息并封装到 MethodInfo 对象中。
  4. 将第 1、2 步骤中组装的 ProxyMethod 对象进行解析,生成代表该方法字段标识的 FieldInfo 对象并缓存起来,解析为该方法的字节码信息的 MethodInfo 对象并缓存起来。
  5. 根据第 1、2 步骤中组装的 ProxyMethod 对象生成静态代码块字节码信息并封装到 MethodInfo 对象中并缓存起来。
  6. 对代理类中的方法数量做限制。
  7. 依次将代理类相关类路径、接口路径、代理类名、Proxy名、接口名、FieldInfo信息、MehodInfo信息写入到字节输出流中。
  8. 生成代理类字节码数组

整个过程介绍完毕,下面对该过程中涉及的核心方法进行说明。

addProxyMethod

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
+--- ProxyGenerator

/**
* 将方法对象拆解,组装成 ProxyMethod 对象
*
* @param var1 方法对象
* @param var2 方法所在接口/类
*/
private void addProxyMethod(Method var1, Class<?> var2) {
// 1 方法名
String var3 = var1.getName();
// 2 方法参数类型
Class[] var4 = var1.getParameterTypes();
// 3 方法返回类型
Class var5 = var1.getReturnType();
// 4 方法异常类型
Class[] var6 = var1.getExceptionTypes();

// 5 方法签名,如 hashCode()、equals(Ljava/lang/Object;),最为缓存的 key
String var7 = var3 + getParameterDescriptors(var4);

Object var8 = (List) this.proxyMethods.get(var7);
if (var8 != null) {
Iterator var9 = ((List) var8).iterator();

while (var9.hasNext()) {
ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod) var9.next();
if (var5 == var10.returnType) {
ArrayList var11 = new ArrayList();
collectCompatibleTypes(var6, var10.exceptionTypes, var11);
collectCompatibleTypes(var10.exceptionTypes, var6, var11);
var10.exceptionTypes = new Class[var11.size()];
var10.exceptionTypes = (Class[]) var11.toArray(var10.exceptionTypes);
return;
}
}
} else {
var8 = new ArrayList(3);
this.proxyMethods.put(var7, var8);
}

// 创建 ProxyMethod 对象,并保存到 proxyMethods 中
((List) var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2));
}

上述方法用于将 Method 对象拆解、组装为 ProxyMethod 对象,为后续组装代理类的方法字节码做准备。

generateConstructor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+--- ProxyGenerator
private ProxyGenerator.MethodInfo generateConstructor() throws IOException {
// 创建构造方法字节码的 MethodInfo
ProxyGenerator.MethodInfo var1 = new ProxyGenerator.MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V", 1);

// 写入 MethodInfo 的输出流中
DataOutputStream var2 = new DataOutputStream(var1.code);
this.code_aload(0, var2);
this.code_aload(1, var2);
var2.writeByte(183);
var2.writeShort(this.cp.getMethodRef("java/lang/reflect/Proxy", "<init>", "(Ljava/lang/reflect/InvocationHandler;)V"));
var2.writeByte(177);
var1.maxStack = 10;
var1.maxLocals = 2;
var1.declaredExceptions = new short[0];
return var1;
}

generateConstructor 方法用于生成构造方法的字节码信息。

generateStaticInitializer

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
+--- ProxyGenerator
private ProxyGenerator.MethodInfo generateStaticInitializer() throws IOException {
ProxyGenerator.MethodInfo var1 = new ProxyGenerator.MethodInfo("<clinit>", "()V", 8);
byte var2 = 1;
byte var4 = 0;
DataOutputStream var6 = new DataOutputStream(var1.code);

// 获取方法列表
Iterator var7 = this.proxyMethods.values().iterator();
// 根据方法信息初始化对应的字段
while (var7.hasNext()) {
List var8 = (List) var7.next();
Iterator var9 = var8.iterator();

while (var9.hasNext()) {
ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod) var9.next();
// 方法字段初始化
var10.codeFieldInitialization(var6);
}
}

// 异常处理
var6.writeByte(177);
short var3;
short var5 = var3 = (short) var1.code.size();
var1.exceptionTable.add(new ProxyGenerator.ExceptionTableEntry(var4, var5, var3, this.cp.getClass("java/lang/NoSuchMethodException")));
this.code_astore(var2, var6);
var6.writeByte(187);
var6.writeShort(this.cp.getClass("java/lang/NoSuchMethodError"));
var6.writeByte(89);
this.code_aload(var2, var6);
var6.writeByte(182);
var6.writeShort(this.cp.getMethodRef("java/lang/Throwable", "getMessage", "()Ljava/lang/String;"));
var6.writeByte(183);
var6.writeShort(this.cp.getMethodRef("java/lang/NoSuchMethodError", "<init>", "(Ljava/lang/String;)V"));
var6.writeByte(191);
var3 = (short) var1.code.size();
var1.exceptionTable.add(new ProxyGenerator.ExceptionTableEntry(var4, var5, var3, this.cp.getClass("java/lang/ClassNotFoundException")));
this.code_astore(var2, var6);
var6.writeByte(187);
var6.writeShort(this.cp.getClass("java/lang/NoClassDefFoundError"));
var6.writeByte(89);
this.code_aload(var2, var6);
var6.writeByte(182);
var6.writeShort(this.cp.getMethodRef("java/lang/Throwable", "getMessage", "()Ljava/lang/String;"));
var6.writeByte(183);
var6.writeShort(this.cp.getMethodRef("java/lang/NoClassDefFoundError", "<init>", "(Ljava/lang/String;)V"));
var6.writeByte(191);
if (var1.code.size() > 65535) {
throw new IllegalArgumentException("code size limit exceeded");
} else {
var1.maxStack = 10;
var1.maxLocals = (short) (var2 + 1);
var1.declaredExceptions = new short[0];
return var1;
}
}

generateStaticInitializer 方法用于生成代理类的静态代码块信息。

至此,JDK 动态代理的整个流程介绍完毕。

小结

JDK 动态代理是针对接口做的代理,目标类没有实现接口是无法通过这个方式创建代理对象。通过拼接字节码生成类是十分灵活的,理论上不管是有接口的类还是普通类都是可以实现代理的,CGLIB 就是通过拼接字节码来实现非接口类的代理逻辑。