ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AOP动态代理详解

AOP动态代理详解

JDK动态代理

AccountService proxy = Proxy.newProxyInstance(...) │ │ proxy 是内存中自动生成的一个类 │ 它实现了 AccountService 接口 │ ▼ ┌───────────────────────────────┐ │ $Proxy0(内存中自动生成的类) │ │ │ │ implements AccountService { │ │ │ │ InvocationHandler handler; │ ← 持有调用处理器 │ │ │ Account deposit(...) { │ │ return handler.invoke( │ ← 每次调用都走 handler │ this, method, args │ │ ); │ │ } │ │ │ │ Account withdraw(...) { │ │ return handler.invoke( │ │ this, method, args │ │ ); │ │ } │ │ │ │ } │ └───────────────────────────────┘ │ 调用 proxy.deposit(1, 5000) │ ▼ handler.invoke(proxy, deposit方法, [1, 5000]) │ ┌───────────┼───────────────┐ │ │ │ ▼ ▼ ▼ Before target.deposit() After 通知 (目标方法) 通知

CGLIB代理

AccountService(原始类) │ │ CGLIB 创建一个子类继承它 │ ▼ AccountService$$EnhancerByCGLIB(自动生成的子类,也就是代理) │ │ 继承了 AccountService │ 重写了所有非 final 方法 │ └── deposit(Integer id, Double amount) { │ ├── Before 逻辑 │ ├── super.deposit(id, amount) ← 调用父类(原始类)的方法 │ ├── After 逻辑 │ └── return result; } 关键区别: - JDK代理:代理和目标对象是"兄弟"关系(实现同一接口) - CGLIB代理:代理和目标对象是"父子"关系(代理继承目标类)

对比

┌─────────────────┬──────────────────────┬──────────────────────┐ │ │ JDK 动态代理 │ CGLIB 代理 │ ├─────────────────┼──────────────────────┼──────────────────────┤ │ │ │ │ │ 前提条件 │ 目标类必须实现接口 │ 不需要接口 │ │ │ │ 目标类不能是 final │ │ │ │ │ ├─────────────────┼──────────────────────┼──────────────────────┤ │ │ │ │ │ 实现方式 │ 实现相同接口 │ 继承目标类 │ │ │ 代理是目标的"兄弟" │ 代理是目标的"儿子" │ │ │ │ │ ├─────────────────┼──────────────────────┼──────────────────────┤ │ │ │ │ │ 核心API │ java.lang.reflect │ net.sf.cglib.proxy │ │ │ .Proxy │ .Enhancer │ │ │ .InvocationHandler │ .MethodInterceptor │ │ │ │ │ ├─────────────────┼──────────────────────┼──────────────────────┤ │ │ │ │ │ 调用方式 │ method.invoke( │ methodProxy. │ │ │ target, args) │ invokeSuper( │ │ │ │ obj, args) │ │ │ │ │ ├─────────────────┼──────────────────────┼──────────────────────┤ │ │ │ │ │ 生成的类名 │ com.sun.proxy │ AccountService │ │ │ .$Proxy0 │ $$EnhancerByCGLIB │ │ │ │ $$xxxxx │ │ │ │ │ ├─────────────────┼──────────────────────┼──────────────────────┤ │ │ │ │ │ 性能 │ 创建快,调用稍慢 │ 创建慢(生成子类) │ │ │ │ 调用快(直接调父类) │ │ │ │ │ ├─────────────────┼──────────────────────┼──────────────────────┤ │ │ │ │ │ 局限性 │ 只能代理实现了接口 │ 不能代理 final 类 │ │ │ 的类 │ 不能代理 final 方法 │ │ │ │ │ └─────────────────┴──────────────────────┴──────────────────────┘ Spring 怎么选择? - Spring 5.x 默认使用 CGLIB(即使目标类实现了接口) - Spring Boot 2.x 开始默认也是 CGLIB - 原因:CGLIB 不需要接口,使用更方便 - 可以通过配置强制使用 JDK 代理:@EnableAspectJAutoProxy(proxyTargetClass = false)
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进