一句话总览:JDK 动态代理基于接口;CGLIB 基于继承子类,Spring AOP 默认优先 JDK,目标无接口时自动切 CGLIB(SpringBoot 2.x + 可配置强制 CGLIB)
对比表
关键细节
1. JDK 动态代理示例要点
// 接口
public interface UserService {
void addUser();
}
// 目标实现类
class UserServiceImpl implements UserService {
@Override
public void addUser() {}
}
// 代理逻辑:InvocationHandler
InvocationHandler handler = (proxy, method, args) -> {
// 前置增强:权限校验、日志、安全鉴权
System.out.println("JDK代理前置");
Object res = method.invoke(target, args);
System.out.println("JDK代理后置");
return res;
};
// 创建代理
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
✅ 只能代理接口方法;你直接调用实现类里不属于接口的方法,代理拦截不到。
2. CGLIB 代理示例要点
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, methodProxy) -> {
System.out.println("CGLIB前置增强");
// 调用父类原方法(重点:methodProxy.invokeSuper,不要method.invoke)
Object res = methodProxy.invokeSuper(obj, args);
System.out.println("CGLIB后置增强");
return res;
});
UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
⚠️ 坑:
final class、final method无法拦截private方法不能被子类重写,不会进入 intercept不要写
method.invoke(target, args)会绕过 CGLIB 子类,直接调用原对象,失去代理效果
和你上一个问题【动态代理增强安全性】结合
安全增强场景(鉴权、参数校验、防篡改、审计日志、权限拦截):
如果业务层统一面向接口编程:优先 JDK 代理,轻量、无第三方依赖
遗留代码 / 第三方 SDK 类,没有接口:只能用 CGLIB
注意:CGLIB 不能代理 final 类,很多安全框架、工具类是 final,这时方案要改成装饰器模式
Spring AOP 配置补充(SpringBoot3 / Java17)
Java17 强模块化 + 封镜(encapsulation),CGLIB/ASM 会碰到 illegal reflective access:
# 强制Spring使用CGLIB代理(默认:目标有接口用JDK)
spring.aop.proxy-target-class=true
Java17 环境下使用 CGLIB,部分场景需要 JVM 参数开放模块权限,生产上如果可控,优先面向接口 + JDK 代理更稳。
本文原创作者:易君召,详见:https://www.yijunzhao.cc/about,转载请注明出处。
原文链接
欢迎访问