跳转至

JDK 动态代理与 CGLIB 实现的区别?为什么 CGLIB 可以对接口实现代理?

一、核心区别

维度 JDK 动态代理 CGLIB
实现基础 接口 继承(生成子类)
代理对象 实现相同接口 继承目标类
要求 目标必须有接口 目标不能是 final
方法限制 只能代理接口方法 不能代理 final 方法
性能 创建快,调用稍慢 创建慢,调用快
依赖 JDK 自带 ASM 第三方库

二、JDK 动态代理原理

Proxy.newProxyInstance 在运行时生成一个实现了目标接口的类(如 $Proxy0),它持有 InvocationHandler,所有方法调用转给 handler。

public final class $Proxy0 extends Proxy implements UserService {
    public void save() {
        handler.invoke(this, m1, null);
    }
}

三、CGLIB 原理

用 ASM 生成目标类的子类,重写非 final 方法,在方法前后插入拦截逻辑。

public class UserServiceImpl$$EnhancerByCGLIB extends UserServiceImpl {
    private MethodInterceptor h;
    public void save() {
        h.intercept(this, m, args, proxy);
    }
}

四、关于"CGLIB 可以对接口代理"

题目问:为什么 CGLIB 方式可以对接口实现代理?

这个说法不准确。CGLIB 的核心是继承,天生是为类设计的:

  • CGLIB 不能"只代理接口",它需要一个具体类来继承。
  • 真正能代理接口的是 JDK 动态代理

CGLIB 也可以和接口配合使用(目标类实现了某接口,CGLIB 生成的子类也自然实现了该接口),但这不是 CGLIB 的"能力",而是继承的副作用。

正确理解: - 想代理接口 → 用 JDK 动态代理。 - 想代理类(无接口)→ 用 CGLIB。

五、Spring 的选择

@Configuration
public class AopConfig {
    // 有接口 -> JDK 动态代理
    // 无接口 -> CGLIB
}

Spring Boot 2.x 默认强制 CGLIB(proxyTargetClass=true),因为 CGLIB 不需要接口,更简单。

常见误区

  • "CGLIB 能代理接口"是错的。它代理的是类,接口只是附带实现。
  • JDK 动态代理必须有接口,CGLIB 必须有可继承的类。