cglib 和 Java 动态代理有什么关系?
cglib 是一个代码生成库,它通过在运行时生成目标类的子类来实现方法拦截,因此可以对没有实现任何接口的普通类做代理。这与 JDK 动态代理必须基于接口的机制形成互补:有接口时 JDK 动态代理够用,没有接口时通常就需要 cglib 这类方案。Spring AOP 等框架正是根据目标类是否实现接口,在两种代理方式之间选择。
cglib 的代理机制
cglib(Code Generation Library)的核心能力是动态生成字节码。用于代理时,它的基本思路是:
- 读取目标类的字节码信息;
- 生成一个继承目标类的子类;
- 在子类中覆盖需要拦截的方法,把调用转发给回调(如
MethodInterceptor); - 由这个子类实例代替原对象对外提供服务。
因为代理对象是目标类的子类,所以调用方感知不到差别,方法调用会先进入拦截逻辑,再由拦截逻辑决定是否调用父类(原始)实现。
与 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 上的仓库为准。