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)