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 的选择¶
Spring Boot 2.x 默认强制 CGLIB(proxyTargetClass=true),因为 CGLIB 不需要接口,更简单。
常见误区
- "CGLIB 能代理接口"是错的。它代理的是类,接口只是附带实现。
- JDK 动态代理必须有接口,CGLIB 必须有可继承的类。