Skip to content
进阶

一句话答案

JDK 代理基于接口(Proxy+InvocationHandler),CGLIB 基于继承(字节码生成子类),Spring 默认有接口用 JDK 否则用 CGLIB。

核心要点

AOP(Aspect-Oriented Programming,面向切面编程):

  • 通过代理模式,在不修改原始代码的情况下,在方法调用前后插入横切逻辑(日志、事务、权限等)

Spring AOP 的两种代理机制:

1. JDK 动态代理(interface-based)

java
// 要求:目标类必须实现至少一个接口
// 原理:运行时生成实现了相同接口的代理类

interface UserService { void save(); }

class UserServiceImpl implements UserService { ... }

// JDK 动态代理生成的代理类(伪代码):
class $Proxy0 implements UserService {
    InvocationHandler h;
    public void save() {
        h.invoke(this, saveMethod, args);  // 调用切面逻辑
    }
}

2. CGLIB(subclass-based)

java
// 不要求接口,通过继承生成目标类的子类(代理类)
// 原理:字节码增强,ASM 生成子类,重写所有方法

class UserServiceImpl {
    public void save() { ... }
}

// CGLIB 生成的代理类(伪代码):
class UserServiceImpl$$EnhancerByCGLIB extends UserServiceImpl {
    @Override
    public void save() {
        // 切面逻辑(前置通知)
        super.save();  // 调用原方法
        // 切面逻辑(后置通知)
    }
}

JDK 动态代理 vs CGLIB 对比:

维度JDK 动态代理CGLIB
要求目标类必须有接口无要求(但不能代理 final 类/方法)
实现方式反射 + Proxy.newProxyInstanceASM 字节码操作,生成子类
性能调用时反射,略慢(JDK 8 后差距减小)直接调用子类方法,更快
Spring 默认有接口 → JDK(Spring Boot 2.x 后默认改为 CGLIB)无接口 → CGLIB
Spring Bootspring.aop.proxy-target-class=true(默认)→ 强制用 CGLIB

AOP 相关概念:

Aspect(切面)   = 横切逻辑的模块(如日志切面、事务切面)
JoinPoint(连接点)= 可以被拦截的方法执行点
Pointcut(切入点)= 选择哪些 JoinPoint 被拦截(表达式:execution(* com..*Service.*(..)))
Advice(通知)   = 在 JoinPoint 执行的动作(Before/After/Around/AfterReturning/AfterThrowing)
追问与易错

追问方向:

  • Boot 2.x 默认用哪个?
  • CGLIB 代理 final 类会怎样?
  • InvocationHandler 的三个参数?

易错点:

  • ❌ 有接口就一定用 JDK——Boot 2.x 默认 CGLIB
  • ❌ CGLIB 是字节码编织——是运行时生成子类

💡 记忆锚点

JDK代理 = 替身演员(必须穿同款戏服=实现接口),CGLIB = 克隆人(继承本体生成子类,但不能克隆final)。Spring Boot 2.x后默认用克隆人(CGLIB),不管你有没有戏服。