JDK动态代理要求目标类实现接口,通过反射生成接口实现类并拦截方法调用;CGLIB代理无接口限制,通过ASM生成子类重写非final方法来增强,但不支持final类或方法。

核心在于“不改原有代码,让增强逻辑自动生效”。动态代理不是硬编码切面,而是运行时生成一个新对象——它既实现了目标行为,又包裹了横切逻辑。JDK 与 CGLIB 是两种生成方式,选哪个,取决于目标类有没有接口、是否允许被继承。
JDK 动态代理:接口是前提,轻量且原生
只适用于目标类实现了至少一个接口的场景。代理对象本质上是该接口的新实现类,所有方法调用都会被统一拦截到 InvocationHandler 的 invoke 方法中。
- 定义好业务接口(如 UserService)和其实现类(UserServiceImpl)
- 写一个 InvocationHandler 实现类,在 invoke 中编写前置(如日志开始)、后置(如统计耗时)、异常处理等逻辑
- 用 Proxy.newProxyInstance 创建代理实例,传入类加载器、接口数组、handler 实例
- 后续所有对该接口的调用,都走代理,原实现类仅作为被委托对象存在
CGLIB 动态代理:无接口限制,靠子类继承
当目标类没实现接口,或需代理其非接口方法(如 protected、package-private 方法)时,CGLIB 更合适。它通过字节码技术生成目标类的子类,并重写所有非 final 方法,在方法入口/出口插入增强逻辑。
- 引入 cglib-nodep 依赖(Spring 已内置)
- 创建 MethodInterceptor 实现类,在 intercept 方法中控制执行流程:可选择是否调用 super.invoke(),也可在前后加逻辑
- 用 Enhancer 设置目标类、回调(callback),调用 create() 得到代理子类实例
- 注意:目标类不能是 final,被代理方法也不能是 final,否则 CGLIB 无法覆盖
织入时机与切面组织:从手动到规范
单纯写代理只是基础,真正平滑织入,要解决三个问题:在哪织(Pointcut)、织什么(Advice)、怎么织(Weaving)。Spring AOP 就是把这套流程标准化了:
- 用 @Pointcut 定义表达式(如 execution(* com.example.service..*.*(..))),声明哪些方法需要增强
- 用 @Before、@After、@Around 等注解标记 Advice 方法,明确执行时机
- Spring 自动判断:有接口 → 默认用 JDK 代理;无接口 → 切换为 CGLIB;也可强制指定 proxy-target-class=true 启用 CGLIB
- 最终生成的代理对象,对调用方完全透明,仍按原类型使用(接口类型或具体类类型)
关键注意事项:避免踩坑
两类代理各有边界,忽略约束会导致运行失败或逻辑不生效:
- JDK 代理无法代理目标类自身定义但未在接口中声明的方法
- CGLIB 无法代理 final 类、final 方法、static 方法,也不支持构造器增强
- 代理对象只能拦截 public 方法;private / protected 方法在 CGLIB 中虽可重写,但通常不建议作为切入点
- 若目标对象被多个切面增强,需关注 @Order 或 @Aspect 的优先级顺序,避免执行顺序错乱

















