cglib 和 Java 动态代理有什么关系?

cglib 是一个代码生成库,它通过在运行时生成目标类的子类来实现方法拦截,因此可以对没有实现任何接口的普通类做代理。这与 JDK 动态代理必须基于接口的机制形成互补:有接口时 JDK 动态代理够用,没有接口时通常就需要 cglib 这类方案。Spring AOP 等框架正是根据目标类是否实现接口,在两种代理方式之间选择。

cglib 的代理机制

cglib(Code Generation Library)的核心能力是动态生成字节码。用于代理时,它的基本思路是:

  1. 读取目标类的字节码信息;
  2. 生成一个继承目标类的子类;
  3. 在子类中覆盖需要拦截的方法,把调用转发给回调(如 MethodInterceptor);
  4. 由这个子类实例代替原对象对外提供服务。

因为代理对象是目标类的子类,所以调用方感知不到差别,方法调用会先进入拦截逻辑,再由拦截逻辑决定是否调用父类(原始)实现。

与 JDK 动态代理的关键差异

维度 JDK 动态代理 cglib
实现基础 接口 继承目标类、生成子类
前提条件 目标类必须实现接口 目标类不能是 final,被代理方法不能是 final/private
代理对象类型 实现了目标接口的类 目标类的子类
典型使用方 JDK 自带 java.lang.reflect.Proxy Spring AOP(目标无接口时)等框架

一个直观例子:如果有一个 UserService 类没有实现任何接口,用 JDK 动态代理就无法直接为它创建代理;而 cglib 可以生成 UserService 的子类,在子类里插入日志、事务等逻辑,再调用父类方法。

适用场景与限制

适合用 cglib 的情况:

  • 目标类没有实现接口,但仍需要方法级拦截;
  • 使用 Spring AOP 且未强制走 JDK 代理;
  • 需要覆盖具体类的方法行为。

需要注意的限制:

  • final 类无法被继承,因此不能被 cglib 代理;
  • final 方法和 private 方法无法被覆盖,拦截不到;
  • 生成子类和字节码有一定开销,通常配合缓存使用。

关于 cglib.sourceforge.net

如果你打开 cglib.sourceforge.net 被自动跳转,是因为 cglib 已不再托管在 SourceForge,页面本身只保留了跳转提示:cglib is now hosted on github。也就是说,该域名现在的作用是引导访问者前往 GitHub 上的新托管地址,而不是继续提供下载。需要获取源码或版本信息时,应以 GitHub 上的仓库为准。

cglib.sourceforge.net