JDK 动态代理与 CGLIB 代理详解
本文档系统梳理 JDK 动态代理与 CGLIB 代理的底层原理、源码实现、使用场景、常见坑点及横向对比。
目录
一、动态代理概述 1.1 什么是动态代理 动态代理是一种在运行时动态生成代理类 的设计模式,代理对象可以在不修改原始类代码的前提下,对目标方法进行增强(前置处理、后置处理、异常处理等)。
1.2 为什么需要动态代理
解耦 :业务逻辑与横切关注点(日志、事务、权限、监控)分离
无侵入 :不需要修改原始类代码
灵活 :运行时动态生成,编译期不需要额外文件
AOP 基础 :Spring AOP、MyBatis Mapper、Dubbo 等框架的底层基石
1.3 主流实现分类
类型
代表技术
核心特点
基于接口
JDK 动态代理
目标类必须实现接口
基于继承
CGLIB、ByteBuddy
不需要接口,生成目标类子类
基于字节码
ASM、Javassist
直接操作字节码,底层框架使用
二、JDK 动态代理 2.1 核心原理 JDK 动态代理是 JDK 原生提供 的代理机制,位于 java.lang.reflect 包下。
核心机制 :
运行时动态生成一个代理类,该类继承自 Proxy
代理类实现目标对象的所有接口
通过 InvocationHandler 回调接口实现方法增强
底层基于 ASM 字节码框架生成代理类字节码
为什么必须有接口?
Java 是单继承语言,代理类已经继承了 Proxy
因此只能通过实现接口 的方式来定义代理的行为
没有接口就无法描述代理对象能做什么
2.2 核心 API 2.2.1 Proxy 类
核心方法:
1 2 3 4 5 6 7 8 9 10 11 12 public static Object newProxyInstance ( ClassLoader loader, // 类加载器,通常用目标类的类加载器 Class<?>[] interfaces, // 目标类实现的接口数组 InvocationHandler h // 调用处理器,增强逻辑写在这里 ) public static boolean isProxyClass (Class<?> cl) public static InvocationHandler getInvocationHandler (Object proxy)
2.2.2 InvocationHandler 接口 1 2 3 4 5 6 7 8 9 10 11 public interface InvocationHandler { Object invoke (Object proxy, Method method, Object[] args) throws Throwable; }
2.3 源码分析 2.3.1 newProxyInstance 执行流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 newProxyInstance(loader, interfaces, h) │ ├─ 1. 安全检查:校验接口访问权限 │ ├─ 2. 获取/生成代理类 Class 对象 │ └─ getProxyClass0(loader, interfaces) │ └─ 缓存中存在?→ 直接返回 │ └─ 不存在 → ProxyClassFactory 生成 │ ├─ 3. 获取代理类的构造器(参数为 InvocationHandler) │ └─ cons = cl.getConstructor(InvocationHandler.class) │ └─ 4. 反射创建代理实例 └─ cons.newInstance(new Object[]{h})
2.3.2 代理类生成关键代码(简化版) 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 private static final class ProxyClassFactory implements BiFunction <ClassLoader, Class<?>[], Class<?>> { @Override public Class <?> apply (ClassLoader loader, Class<?>[] interfaces) { for (Class<?> intf : interfaces) { } long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + "$Proxy" + num; byte [] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags ); return defineClass0(loader, proxyName, proxyClassFile, 0 , proxyClassFile.length); } }
2.3.3 生成的代理类长什么样? 生成的 $Proxy0 类(反编译后)大致结构:
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 public final class $Proxy0 extends Proxy implements UserService { private static Method m0; private static Method m1; private static Method m2; private static Method m3; static { try { m0 = Class.forName("java.lang.Object" ).getMethod("hashCode" ); m1 = Class.forName("java.lang.Object" ).getMethod("equals" , Class.forName("java.lang.Object" )); m2 = Class.forName("java.lang.Object" ).getMethod("toString" ); m3 = Class.forName("com.example.UserService" ).getMethod("addUser" ); } catch (NoSuchMethodException e) { throw new NoSuchMethodError (e.getMessage()); } } public $Proxy0 (InvocationHandler h) { super (h); } @Override public void addUser () { try { super .h.invoke(this , m3, null ); } catch (Error | RuntimeException e) { throw e; } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } @Override public int hashCode () { try { return (Integer) super .h.invoke(this , m0, null ); } catch (Throwable e) { throw new UndeclaredThrowableException (e); } } }
关键结论 :
代理类继承 Proxy,实现所有目标接口
每个接口方法都对应一个静态 Method 对象
方法调用全部转发给 InvocationHandler.invoke()
受检异常会被包装成 UndeclaredThrowableException
2.4 使用案例 2.4.1 基础示例:日志增强 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 public interface UserService { void addUser (String name) ; String getUserById (Long id) ; } public class UserServiceImpl implements UserService { @Override public void addUser (String name) { System.out.println("添加用户:" + name); } @Override public String getUserById (Long id) { System.out.println("查询用户:" + id); return "User-" + id; } } public class LogInvocationHandler implements InvocationHandler { private final Object target; public LogInvocationHandler (Object target) { this .target = target; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("【日志前置】调用方法:" + method.getName()); long start = System.currentTimeMillis(); Object result = method.invoke(target, args); long cost = System.currentTimeMillis() - start; System.out.println("【日志后置】方法 " + method.getName() + " 执行耗时:" + cost + "ms" ); return result; } } public class JdkProxyDemo { public static void main (String[] args) { UserService target = new UserServiceImpl (); UserService proxy = (UserService) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new LogInvocationHandler (target) ); proxy.addUser("张三" ); String user = proxy.getUserById(1001L ); System.out.println("返回结果:" + user); } }
输出结果 :
1 2 3 4 5 6 7 【日志前置】调用方法:addUser 添加用户:张三 【日志后置】方法 addUser 执行耗时:0ms 【日志前置】调用方法:getUserById 查询用户:1001 【日志后置】方法 getUserById 执行耗时:0ms 返回结果:User-1001
2.4.2 通用代理工厂(封装) 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 JdkProxyFactory { @SuppressWarnings("unchecked") public static <T> T createProxy (T target, Runnable before, Runnable after) { return (T) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxy, method, args) -> { before.run(); Object result = method.invoke(target, args); after.run(); return result; } ); } public static void main (String[] args) { UserService target = new UserServiceImpl (); UserService proxy = JdkProxyFactory.createProxy( target, () -> System.out.println("=== before ===" ), () -> System.out.println("=== after ===" ) ); proxy.addUser("test" ); } }
2.4.3 错误示例:无接口强行代理 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 class OrderService { public void createOrder () { System.out.println("执行创建订单" ); } } public class JdkProxyFailDemo { public static void main (String[] args) { OrderService target = new OrderService (); Class <?>[] interfaces = target.getClass().getInterfaces(); System.out.println("接口数量:" + interfaces.length); Object proxyObj = Proxy.newProxyInstance( target.getClass().getClassLoader(), interfaces, (proxy, method, args1) -> { System.out.println("【代理前置】" ); return method.invoke(target, args1); } ); OrderService proxy = (OrderService) proxyObj; proxy.createOrder(); } }
报错信息 :
1 2 Exception in thread "main" java.lang.ClassCastException: class jdk.proxy1.$Proxy0 cannot be cast to class com.example.OrderService
2.5 常见坑点
坑点
说明
解决方案
目标类无接口
强转时报 ClassCastException
改用 CGLIB / 抽取接口
强转成实现类
即使有接口,转成实现类也报错
必须转成接口类型
invoke 中调用 proxy
在 invoke 里调用 proxy 的方法会导致死循环递归
用 target 调用真实方法
受检异常包装
接口没声明的受检异常会被包成 UndeclaredThrowableException
在 invoke 中捕获处理,或在接口声明
equals/hashCode/toString
这三个方法也会走 invoke 逻辑
在 invoke 中判断 method 名称做特殊处理
性能
首次生成代理类较慢,反射调用有开销
频繁调用场景可考虑 CGLIB/ByteBuddy
三、CGLIB 代理 3.1 核心原理 CGLIB(Code Generation Library)是一个基于 ASM 的字节码生成库。
核心机制 :
运行时动态生成目标类的子类
在子类中重写 目标类的非 final 方法
方法调用时通过 MethodInterceptor 回调实现增强
底层使用 ASM 直接生成字节码
为什么不需要接口?
CGLIB 采用继承 的方式,代理类是目标类的子类
子类天然拥有父类的所有非私有、非 final 方法
因此不需要目标类实现任何接口
注意 :CGLIB 项目已停止维护,新项目推荐使用 ByteBuddy 替代。
3.2 核心 API 3.2.1 Enhancer 类 CGLIB 的核心类,相当于 JDK 代理中的 Proxy。
1 net.sf.cglib.proxy.Enhancer
常用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void setSuperclass (Class superclass) public void setCallback (Callback callback) public void setCallbacks (Callback[] callbacks) public Object create () public Object create (Class[] argumentTypes, Object[] arguments)
3.2.2 MethodInterceptor 接口 1 2 3 4 5 6 7 8 9 10 11 12 public interface MethodInterceptor extends Callback { Object intercept (Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable; }
3.2.3 MethodProxy 类 1 net.sf.cglib.proxy.MethodProxy
1 2 3 4 5 public Object invokeSuper (Object obj, Object[] args) throws Throwablepublic Object invoke (Object obj, Object[] args) throws Throwable
3.3 源码分析 3.3.1 Enhancer.create() 执行流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Enhancer.create() │ ├─ 1. 校验:父类不能是 final │ ├─ 2. 生成代理类名 │ └─ 格式:父类名$$EnhancerByCGLIB$$随机哈希 │ 例如:PayService$$EnhancerByCGLIB$$1a2b3c4d │ ├─ 3. 使用 DefaultGeneratorStrategy 生成字节码 │ └─ 底层通过 ASM 写字节码 │ ├─ 生成类:继承目标类 │ ├─ 为每个非 final 方法生成重写版本 │ ├─ 每个方法对应一个 CGLIB$xxx 原始方法引用 │ └─ 方法内部调用 intercept() │ ├─ 4. 加载字节码 → Class 对象 │ └─ 5. 反射创建实例,设置 Callback
3.3.2 生成的代理类结构(简化) 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 public class PayService$$EnhancerByCGLIB$$1a2b3c4d extends PayService { private MethodInterceptor CGLIB$CALLBACK_0; private static final Method CGLIB$pay$0 $Method; private static final MethodProxy CGLIB$pay$0 $Proxy; static { CGLIB$pay$0 $Method = ... CGLIB$pay$0 $Proxy = ... } @Override public void pay (Long amount) { MethodInterceptor interceptor = this .CGLIB$CALLBACK_0; if (interceptor != null ) { interceptor.intercept(this , CGLIB$pay$0 $Method, new Object []{amount}, CGLIB$pay$0 $Proxy); } else { super .pay(amount); } } final void CGLIB$pay$0 (Long amount) { super .pay(amount); } }
关键结论 :
代理类是目标类的子类
非 final 方法被重写,内部调用 MethodInterceptor.intercept()
final 方法无法被重写,直接继承父类实现,不会走拦截逻辑
MethodProxy.invokeSuper() 比反射 method.invoke() 性能更好
3.4 使用案例 3.4.1 基础示例:无接口类的代理 1 2 3 4 5 6 {/* Maven 依赖 */} <dependency > <groupId > cglib</groupId > <artifactId > cglib</artifactId > <version > 3.3.0</version > </dependency >
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 public class PayService { public void pay (Long amount) { System.out.println("执行支付,金额:" + amount); } public final void printInfo () { System.out.println("这是 final 方法" ); } } public class LogMethodInterceptor implements MethodInterceptor { @Override public Object intercept (Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("【CGLIB前置】调用方法:" + method.getName()); long start = System.currentTimeMillis(); Object result = proxy.invokeSuper(obj, args); long cost = System.currentTimeMillis() - start; System.out.println("【CGLIB后置】方法 " + method.getName() + " 耗时:" + cost + "ms" ); return result; } } public class CglibProxyDemo { public static void main (String[] args) { Enhancer enhancer = new Enhancer (); enhancer.setSuperclass(PayService.class); enhancer.setCallback(new LogMethodInterceptor ()); PayService proxy = (PayService) enhancer.create(); proxy.pay(1000L ); System.out.println("\n--- 调用 final 方法 ---" ); proxy.printInfo(); } }
输出结果 :
1 2 3 4 5 6 【CGLIB前置】调用方法:pay 执行支付,金额:1000 【CGLIB后置】方法 pay 耗时:0ms --- 调用 final 方法 --- 这是 final 方法
3.4.2 CGLIB 代理工厂(通用封装) 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 public class CglibProxyFactory { @SuppressWarnings("unchecked") public static <T> T createProxy (Class<T> targetClass, MethodInterceptor interceptor) { Enhancer enhancer = new Enhancer (); enhancer.setSuperclass(targetClass); enhancer.setCallback(interceptor); return (T) enhancer.create(); } public static void main (String[] args) { PayService proxy = CglibProxyFactory.createProxy( PayService.class, (obj, method, args1, proxy1) -> { System.out.println("=== before ===" ); Object result = proxy1.invokeSuper(obj, args1); System.out.println("=== after ===" ); return result; } ); proxy.pay(500L ); } }
3.4.3 回调过滤器:不同方法走不同拦截逻辑 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 public class CallbackFilterDemo { public static void main (String[] args) { Enhancer enhancer = new Enhancer (); enhancer.setSuperclass(PayService.class); Callback logInterceptor = new LogMethodInterceptor (); Callback noOpInterceptor = NoOp.INSTANCE; enhancer.setCallbacks(new Callback []{logInterceptor, noOpInterceptor}); enhancer.setCallbackFilter(method -> { if ("pay" .equals(method.getName())) { return 0 ; } return 1 ; }); PayService proxy = (PayService) enhancer.create(); proxy.pay(100L ); proxy.printInfo(); } }
3.5 常见坑点
坑点
说明
解决方案
final 类无法代理
CGLIB 生成子类,final 类不能被继承
改用 JDK 代理 / 去掉 final
final 方法无法拦截
final 方法不能被重写,增强失效
去掉 final / 换 JDK 代理(接口方法)
private 方法无法代理
子类无法访问父类私有方法
改为 protected / public
构造器调用两次
CGLIB 创建代理时可能触发两次构造
构造器中不要有副作用逻辑
用错 invoke 方法
用 method.invoke(target, args) 会导致循环
必须用 methodProxy.invokeSuper(obj, args)
CGLIB 已停止维护
官方不再更新,不兼容某些新特性
新项目考虑 ByteBuddy
四、横向对比 4.1 核心对比表
对比维度
JDK 动态代理
CGLIB 代理
底层实现
JDK 原生,基于 ASM 生成字节码
第三方库,基于 ASM 生成字节码
代理方式
实现目标接口
继承目标类(生成子类)
是否需要接口
✅ 必须 实现至少一个接口
❌ 不需要接口
是否需要依赖
❌ JDK 自带,无需额外依赖
✅ 需要引入 cglib 包
final 类
不涉及(只代理接口)
❌ 无法代理
final 方法
不涉及(接口方法不能是 final)
❌ 无法拦截增强
private 方法
不涉及(接口方法都是 public)
❌ 无法代理
代理对象类型
接口类型
目标类类型(子类)
核心回调接口
InvocationHandler
MethodInterceptor
调用原方法方式
method.invoke(target, args)(反射)
methodProxy.invokeSuper(obj, args)(更快)
性能(首次)
较慢(生成代理类)
较慢(生成子类)
性能(运行时)
反射调用,中等
方法代理调用,略快于 JDK
Spring AOP 默认
目标有接口时默认使用
目标无接口时使用
代表框架
MyBatis Mapper、Spring AOP(接口模式)
Spring AOP(类模式)、旧版 Hibernate
4.2 性能对比 1 2 3 4 5 6 7 性能排序(方法调用速度): 直接调用 > CGLIB (MethodProxy) > JDK 动态代理 > 纯反射 说明: - CGLIB 使用 MethodProxy.invokeSuper() 比 JDK 的反射调用快约 10%~30% - 但首次生成代理类的开销两者差不多,都需要运行时生成字节码 - 现代 JVM 的反射优化已经很好,实际差距越来越小
4.3 选型建议
场景
推荐方案
理由
目标类有接口
JDK 动态代理
JDK 原生、无依赖、更轻量
目标类无接口
CGLIB / ByteBuddy
只能用继承方式
Spring Boot 项目
用 Spring AOP 即可
框架自动选择,不用关心底层
对性能要求极高
ByteBuddy / ASM
现代字节码工具,性能更好
需要支持 final 类
只能用 JDK 代理(接口)
CGLIB 无法代理 final 类
新项目框架选型
优先考虑 ByteBuddy
CGLIB 已停止维护
五、Spring AOP 中的代理选择 5.1 选择逻辑 Spring AOP 会根据目标对象是否实现接口,自动选择代理方式:
1 2 3 4 5 6 目标对象是否实现接口? │ ├─ 是 → 默认使用 JDK 动态代理 │ (可通过配置强制使用 CGLIB) │ └─ 否 → 使用 CGLIB 代理
5.2 配置方式 Spring Boot 配置 1 2 3 4 5 spring.aop.proxy-target-class =true
XML 配置 1 2 {/* 开启自动代理,默认 false 即优先 JDK 代理 */} <aop:aspectj-autoproxy proxy-target-class ="true" />
Java Config 1 2 3 4 5 @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) public class AppConfig { }
5.3 Spring Boot 2.x / 3.x 变化
Spring Boot 1.x :spring.aop.proxy-target-class 默认 false
Spring Boot 2.x / 3.x :spring.aop.proxy-target-class 默认 true
即默认全部使用 CGLIB 代理
原因:减少因代理类型不同导致的注入问题(注入实现类 vs 注入接口)
5.4 Spring 事务代理失效场景 面试高频:为什么 Spring 事务有时候不生效?
1 2 3 4 5 6 7 8 9 10 11 12 13 @Service public class OrderService { @Transactional public void methodA () { this .methodB(); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB () { } }
原因 :this 是原始对象,不是代理对象,所以 methodB 上的事务注解不生效。
解决方案 :
注入自己:@Autowired private OrderService self; 然后 self.methodB()
使用 AopContext.currentProxy() 获取代理对象
拆分到不同的类中
六、其他动态代理技术简介 6.1 ByteBuddy(现代首选)
定位 :新一代字节码生成与操作库
特点 :
对 ASM 做了高层封装,API 优雅、类型安全
性能接近原生 ASM
支持 Java 8~21+ 新特性(Record、Sealed Class 等)
CGLIB 的替代者 ,CGLIB 已停止维护
使用场景 :Spring Boot 3.x、Mockito、Jackson、Netty 等
1 2 3 4 5 6 7 8 new ByteBuddy () .subclass(PayService.class) .method(ElementMatchers.named("pay" )) .intercept(MethodDelegation.to(LogInterceptor.class)) .make() .load(PayService.class.getClassLoader()) .getLoaded();
6.2 Javassist
定位 :JBoss 出品的字节码编辑库
特点 :
支持用 Java 源码字符串生成类,不需要懂字节码指令
学习成本低
性能略逊于 ASM / ByteBuddy
使用场景 :旧版 Dubbo、Hibernate、JBoss 等
6.3 ASM
定位 :最底层的字节码操作框架
特点 :
直接读写 class 文件二进制
性能天花板
学习成本极高,需要懂 JVM 字节码指令
使用场景 :几乎所有字节码工具的底层(JDK Proxy、CGLIB、ByteBuddy 都基于 ASM)
6.4 技术关系图 1 2 3 4 5 6 7 8 9 10 11 12 13 ┌─────────────────────────────────────┐ │ 业务层动态代理 │ │ (JDK Proxy / CGLIB / 框架封装) │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ 字节码生成工具层 │ │ (ByteBuddy / Javassist / ASM) │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ JVM 字节码 │ └─────────────────────────────────────┘
七、面试高频问答 Q1:JDK 动态代理为什么必须要有接口? 因为 JDK 动态代理生成的代理类已经继承了 Proxy 类,Java 是单继承语言,所以只能通过实现接口 的方式来定义代理对象的行为。没有接口就无法描述代理对象能做什么。
Q2:CGLIB 能代理接口吗? 可以。CGLIB 的 Enhancer 也支持 setInterfaces() 方法,让代理类同时实现指定接口。但这不是 CGLIB 的主要使用场景,有接口时直接用 JDK 代理更轻量。
Q3:JDK 动态代理和 CGLIB 哪个性能更好?
首次生成 :两者差不多,都需要运行时生成字节码
运行时调用 :CGLIB 的 MethodProxy.invokeSuper() 比 JDK 的反射调用略快(约 10%~30%)
现代 JVM :反射优化越来越好,实际差距在缩小
绝大多数业务场景下,性能差异可以忽略,优先考虑适用性
Q4:CGLIB 为什么比 JDK 动态代理快? JDK 动态代理使用 method.invoke(target, args) 进行反射调用,每次调用都需要:
方法权限检查
参数装箱拆箱
反射调用开销
CGLIB 的 MethodProxy.invokeSuper() 直接生成了调用父类方法的字节码,避免了反射的开销,相当于直接调用。
Q5:Spring AOP 默认用哪种代理?
Spring Boot 2.x / 3.x :默认 spring.aop.proxy-target-class=true,即默认全部使用 CGLIB
传统 Spring :目标有接口时默认 JDK 代理,无接口时用 CGLIB
可以通过 proxyTargetClass=true 强制使用 CGLIB
Q6:同一个类中方法调用,为什么事务/缓存注解不生效? 因为 Spring AOP 是基于代理的,只有通过代理对象调用的方法才会走增强逻辑。同类内部调用用的是 this(原始对象),不走代理,所以注解不生效。
解决方法:
注入自己的代理对象
使用 AopContext.currentProxy()
拆分到不同类
Q7:CGLIB 和 ByteBuddy 怎么选?
新项目 :优先选 ByteBuddy,CGLIB 已经停止维护
老项目 :如果已经用了 CGLIB 且没问题,可以继续用
Spring Boot 3.x :内部已经在逐步替换为 ByteBuddy
ByteBuddy 的 API 更现代、更安全,功能也更强大
Q8:动态代理和静态代理有什么区别?
对比维度
静态代理
动态代理
生成时机
编译期
运行期
代理类
手写或编译生成
运行时动态生成
灵活性
低,每个目标类都要写代理
高,一个通用代理可以代理多个类
代码量
多,重复代码多
少,通用逻辑复用
性能
高(直接调用)
略低(反射/字节码)
代表
装饰器模式、AspectJ 编译期织入
JDK Proxy、CGLIB
文档版本 :v1.0最后更新 :2026-08适用范围 :Java 8+ / Spring Boot 2.x / 3.x