# 动态代理两个实例 (视频中学的)

动态代理在 Java 中有着广泛的应用,比如 Spring AOP、Hibernate 数据查询、测试框架的后端 mock、RPC 远程调用、 Java 注解对象获取、日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等。
本文主要介绍 Java 中两种常见的动态代理方式: JDK 原生动态代理和 CGLIB 动态代理。

# 代理模式

本文将介绍的 Java 动态代理与设计模式中的代理模式有关,什么是代理模式呢?

** 代理模式:** 给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。

代理模式角色分为 3 种:

  • Subject(抽象主题角色) :定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法;

  • RealSubject(真实主题角色) :真正实现业务逻辑的类;

  • Proxy(代理主题角色) :用来代理和封装真实主题;

代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层

代理模式按照职责(使用场景)来分类,至少可以分为以下几类:

1
2
3
4
5
6
7
8
1、远程代理。 
2、虚拟代理。
3、Copy-on-Write 代理。
4、保护(Protect or Access)代理。
5、Cache代理。
6、防火墙(Firewall)代理。
7、同步化(Synchronization)代理。
8、智能引用(Smart Reference)代理等等。

如果根据字节码的创建时机来分类,可以分为静态代理和动态代理:

  • 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了。
  • 而动态代理的源码是在程序运行期间由 JVM 根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件

# 静态代理

我们先通过实例来学习静态代理,然后理解静态代理的缺点,再来学习本文的主角:动态代理

编写一个接口 UserService ,以及该接口的一个实现类 UserServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface UserService {
public void select();
public void update();
}

public class UserServiceImpl implements UserService {
public void select() {
System.out.println("查询 selectById");
}
public void update() {
System.out.println("更新 update");
}
}

我们将通过静态代理对 UserServiceImpl 进行功能增强,在调用 selectupdate 之前记录一些日志。写一个代理类 UserServiceProxy ,代理类需要实现 UserService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class UserServiceProxy implements UserService {
private UserService target; // 被代理的对象

public UserServiceProxy(UserService target) {
this.target = target;
}
public void select() {
before();
target.select(); // 这里才实际调用真实主题角色的方法
after();
}
public void update() {
before();
target.update(); // 这里才实际调用真实主题角色的方法
after();
}

private void before() { // 在执行方法之前执行
System.out.println(String.format("log start time [%s] ", new Date()));
}
private void after() { // 在执行方法之后执行
System.out.println(String.format("log end time [%s] ", new Date()));
}
}

客户端测试

1
2
3
4
5
6
7
8
9
public class Client1 {
public static void main(String[] args) {
UserService userServiceImpl = new UserServiceImpl();
UserService proxy = new UserServiceProxy(userServiceImpl);

proxy.select();
proxy.update();
}
}

通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码,这是静态代理的一个优点。

# 静态代理的缺点

虽然静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,静态代理的缺点也会暴露出来。
1、 当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:

- 只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
- 新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类

2、 当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护

# 如何改进?

当然是让代理类动态的生成啦,也就是动态代理。

为什么类可以动态的生成?

这就涉及到 Java 虚拟机的类加载机制了,推荐翻看《深入理解 Java 虚拟机》7.3 节 类加载的过程。

Java 虚拟机类加载过程主要分为五个阶段:加载、验证、准备、解析、初始化。其中加载阶段需要完成以下 3 件事情:

通过一个类的全限定名来获取定义此类的二进制字节流
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据访问入口

由于虚拟机规范对这 3 点要求并不具体,所以实际的实现是非常灵活的,关于第 1 点,获取类的二进制字节流(class 字节码)就有很多途径:

从 ZIP 包获取,这是 JAR、EAR、WAR 等格式的基础
从网络中获取,典型的应用是 Applet
运行时计算生成,这种场景使用最多的是动态代理技术,在 java.lang.reflect.Proxy 类中,就是用了 ProxyGenerator.generateProxyClass 来为特定接口生成形式为 *$Proxy 的代理类的二进制字节流
由其它文件生成,典型应用是 JSP,即由 JSP 文件生成对应的 Class 类
从数据库中获取等等

所以,动态代理就是想办法,根据接口或目标对象,计算出代理类的字节码,然后再加载到 JVM 中使用。但是如何计算?如何生成?情况也许比想象的复杂得多,我们需要借助现有的方案。

# 常见的字节码操作类库

这里有一些介绍:java-source.net/open-source…

# 实现动态代理的思考方向

为了让生成的代理类与目标对象(真实主题角色)保持一致性,从现在开始将介绍以下两种最常见的方式:

  • 通过实现接口的方式 -> JDK 动态代理
  • 通过继承类的方式 -> CGLIB 动态代理

注:使用 ASM 对使用者要求比较高,使用 Javassist 会比较麻烦

# JDk 动态代理

JDK 动态代理主要涉及两个类: java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler ,我们仍然通过案例来学习

编写一个调用逻辑处理器 LogHandler 类,提供日志增强功能,并实现 InvocationHandler 接口;在 LogHandler 中维护一个目标对象,这个对象是被代理的对象(真实主题角色);在 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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;

/**
* @author jinmao
* @create 2022-03-14-10:52
* @ClassName LogHandler
* @Version 1.0
* @Description
*/
public class LogHandler implements InvocationHandler {

/**
* 代理的对象,实际的方法执行者
*/
private Object target;

/**
* 构造方法注入
* @param target
*/
public LogHandler(Object target){
this.target = target;
}

/**
* @param proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
* @param method:我们所要调用某个对象真实的方法的Method对象
* @param args:指代代理对象方法传递的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
//调用target的method方法
Object result = method.invoke(target, args);
after();
return result;
}

// 调用invoke方法之前执行
private void before() {
System.out.printf("log start time [%s] %n", new Date());
}
// 调用invoke方法之后执行
private void after() {
System.out.printf("log end time [%s] %n", new Date());
}

}

编写客户端,获取动态生成的代理类的对象须借助 Proxy 类的 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
import com.cy.learn.dynamic.proxy.utils.ProxyUtils;
import java.lang.reflect.Proxy;
/**
* @creator jinmao
* @create 2022-03-14-13:14
* @ClassName Client
* @Version 1.0
* @Description
*/
public class Client {
public static void main(String[] args) {
// 设置变量可以保存动态代理类,默认名称以 $Proxy0 格式命名
// System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 1. 创建被代理的对象,UserService接口的实现类
UserServiceImpl userServiceImpl = new UserServiceImpl();
// 2. 获取对应的 ClassLoader
ClassLoader classLoader = userServiceImpl.getClass().getClassLoader();
// 3. 获取所有接口的Class,这里的UserServiceImpl只实现了一个接口UserService,
Class<?>[] interfaces = userServiceImpl.getClass().getInterfaces();
// 4. 创建一个将传给代理类的调用请求处理器,处理所有的代理对象上的方法调用
LogHandler logHandler = new LogHandler(userServiceImpl);
/*
5.根据上面提供的信息,创建代理对象 在这个过程中,
a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等同的字节码
b.然后根据相应的字节码转换成对应的class,
c.然后调用newInstance()创建代理实例
*/
UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, interfaces, logHandler);
// 调用代理的方法
proxy.select();
proxy.update();
// 保存JDK动态代理生成的代理类,类名保存为 UserServiceProxy
//ProxyUtils.generateClassFile(userServiceImpl.getClass(), "UserServiceProxy");
}
}

运行结果

1
2
3
4
5
6
log start time [Mon Mar 14 18:02:11 CST 2022] 
查询 selectById
log end time [Mon Mar 14 18:02:11 CST 2022]
log start time [Mon Mar 14 18:02:11 CST 2022]
更新 update
log end time [Mon Mar 14 18:02:11 CST 2022]

InvocationHandlerProxy 的主要方法介绍如下:

java.lang.reflect.InvocationHandler

1
Object invoke(Object proxy, Method method, Object[] args) 定义了代理对象调用方法时希望执行的动作,用于集中处理在动态代理类对象上的方法调用

java.lang.reflect.Proxy

1
2
3
4
static InvocationHandler getInvocationHandler(Object proxy)  用于获取指定代理对象所关联的调用处理器
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 返回指定接口的代理类
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 构造实现指定接口的代理类的一个新实例,所有方法会调用给定处理器对象的 invoke 方法
static boolean isProxyClass(Class<?> cl) 返回 cl 是否为一个代理类

# 代理类的调用过程

生成的代理类到底长什么样子呢?借助下面的工具类,把代理类保存下来再探个究竟

(通过设置环境变量 sun.misc.ProxyGenerator.saveGeneratedFiles=true 也可以保存代理类)

手写一个工具类:

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
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
import java.io.IOException;

/**
* @creator jinmao
* @create 2022-03-14-13:24
* @ClassName ProxyUtils
* @Version 1.0
* @Description
*/
public class ProxyUtils {
/**
* 将根据类信息动态生成的二进制字节码保存到硬盘中,默认的是clazz目录下
* @param clazz: 需要生成动态代理类的类
* @param proxyName: 为动态生成的代理类的名称
*/
public static void generateClassFile(Class clazz, String proxyName) {
// 根据类信息和提供的代理类名称,生成字节码
byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
String paths = clazz.getResource(".").getPath();
System.out.println(paths);
FileOutputStream out = null;
try {
//保留到硬盘中
out = new FileOutputStream(paths + proxyName + ".class");
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

将这两行注释放开即可:

运行结果:

1
2
3
4
5
6
log start time [Mon Mar 14 18:25:57 CST 2022] 
查询 selectById
log end time [Mon Mar 14 18:25:57 CST 2022]
log start time [Mon Mar 14 18:25:57 CST 2022]
更新 update
log end time [Mon Mar 14 18:25:57 CST 2022]

IDEA 再次运行之后就可以在 out 的类路径下找到 UserServiceProxy.class ,双击后 IDEA 的反编译插件会将该二进制 class 文件

UserServiceProxy 的反编译代码:

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
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import com.cy.learn.dynamic.proxy.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

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

public UserServiceProxy(InvocationHandler var1) throws {
super(var1);
}

public final boolean equals(Object var1) throws {
...
}

public final String toString() throws {
...
}

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

public final int hashCode() throws {
...
}

public final void update() throws {
try {
super.h.invoke(this, m3, (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");
m4 = Class.forName("com.cy.learn.dynamic.proxy.UserService").getMethod("select");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("com.cy.learn.dynamic.proxy.UserService").getMethod("update");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}

其中看一段我们自己的和新方法 select() :

1
2
3
4
5
6
7
8
9
public final void select() throws  {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

此处的 super 就是 extendsProxy 类,我们来看 super 中的 h 到底是什么?

super.h.invoke 方法即为我们手写的 LogHandler 中的 invoke 方法,所以日志才会打印

小总结

UserServiceProxy 的代码中我们可以发现:

  • UserServiceProxy 继承了 Proxy 类,并且实现了被代理的所有接口,以及 equals、hashCode、toString 等方法
  • 由于 UserServiceProxy 继承了 Proxy 类,所以每个代理类都会关联一个 InvocationHandler 方法调用处理器
  • 类和所有方法都被 public final 修饰,所以代理类只可被使用,不可以再被继承
  • 每个方法都有一个 Method 对象来描述,Method 对象在 static 静态代码块中创建,以 m + 数字 的格式命名
  • 调用方法的时候通过 super.h.invoke(this, m1, (Object[])null) ; 调用,其中的 super.h.invoke 实际上是在创建代理的时候传递给 Proxy.newProxyInstanceLogHandler 对象,它继承 InvocationHandler 类,负责实际的调用处理逻辑

LogHandlerinvoke 方法接收到 method、args 等参数后,进行一些处理,然后通过反射让被代理的对象 target 执行方法

JDK 动态代理执行方法调用的过程简图如下:

# Proxy.newProxyInstance 源码 debug

代码:

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
/**
* 返回将方法调用分派到指定调用处理程序的指定接口的代理类的实例。
*/
@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);
}

//查找或生成指定的代理类
//此处使用类加载器和接口生成一个Proxy子类字节码对象
Class<?> cl = getProxyClass0(loader, intfs); ------------------------------------> 进入该核心方法

//使用指定的调用处理程序调用其构造函数
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取构造器对象和调用处理器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//用构造器和调用处理器创建proxy代理对象并返回
return cons.newInstance(new Object[]{h});
} catch ...
}

进入 getProxyClass0 方法:

进入到 get 方法

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
// 通过缓存查找值。如果给定的 (key, subKey) 对的缓存中没有条目或条目已被清除
// 则这始终评估subKeyFactory函数并可选地评估valueFactory函数。
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);

expungeStaleEntries();

Object cacheKey = CacheKey.valueOf(key, refQueue);

// 惰性地为特定的cacheKey安装第二级值map
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}

// 创建subKey并从valuesMap中检索由该subKey存储的可能的Supplier<V>
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;

while (true) {
if (supplier != null) {
// supplier可以是一个Factory或者CacheValue<V>实例
V value = supplier.get(); -----------------------------------> 进入该和新方法
if (value != null) {
return value;
}
}
//否则缓存中没有供应商
//或返回null的供应商(可以是一个清除的CacheValue)
//或一个工厂,没有成功安装CacheValue)

// 懒惰地建造工厂
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}

if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}

Supplier.get 方法,此处是 Factory 实现类

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
@Override
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
//在我们等待的时候发生了一些变化:
//可能是我们被CacheValue替换了
//或因为失败而被删除->
//返回null信号WeakCache.get()重试
//循环
return null;
}
// else still us (supplier == this)

// create new value
V value = null;
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter)); ---------> 进入该apply核心方法
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// 断言非空值
assert value != null;

// 使用CacheValue (WeakReference)包装值
CacheValue<V> cacheValue = new CacheValue<>(value);

// put into reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);

// try replacing us with CacheValue (this should always succeed)
if (!valuesMap.replace(subKey, this, cacheValue)) {
throw new AssertionError("Should not reach here");
}

// successfully replaced us with new CacheValue -> return the value
// wrapped by it
return value;
}

apply 源码,此处为 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
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
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
//验证类装入器解析此名称 指向同一个Class对象的接口
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");
}
//验证Class对象实际表示接口。
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
// 确认该接口不是重复的接口。
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}

String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

// 记录非公共代理接口的包,
// 使代理类将在同一个包中定义。
// 验证所有的非公共代理接口都在同一个包中。
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");
}
}
}

if (proxyPkg == null) {
// 如果没有非公共代理接口,使用 com.sun.proxy 包
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}

// 为要生成的代理类选择一个名称
long num = nextUniqueNumber.getAndIncrement();
// 这理解解释了为什么我们每次debug的时候都会有一个 com.sun.proxy.$Proxy0 这种类名
String proxyName = proxyPkg + proxyClassNamePrefix + num;

//生成特定的代理类
//在上面中我讲生成的class文件输出在了out中,就是调用了这个方法,地方使用了AccessController.doPrivileged,为native方法,由C++编写
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//调用native方法
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
//类formaterror表示(禁止代理类生成代码)还有一些其他的 提供给代理的参数的无效方面 类创建(如虚拟机限制超过)
throw new IllegalArgumentException(e.toString());
}
}

小总结

newProxyInstance -> getProxyClass0 -> supplier.get() -> valueFactory.apply 就是生成代理类的大概原理啦,只要有兴趣, debug 一下就可以了。

参考文章

Edited on Views times