cglib无法拦截final类或final方法,因其通过asm生成子类并重写方法实现拦截,而jvm禁止继承final类或覆盖final方法,导致类加载时报verifyerror或illegalargumentexception。

CGLIB 无法拦截 final 类或 final 方法——这不是配置问题,是字节码层面的硬性限制。只要目标类不是 final,且待拦截方法是非 final、非 private 的实例方法,CGLIB 就能通过子类继承 + 方法重写完成拦截。
为什么 final 类和 final 方法一定拦不住
CGLIB 的本质是用 ASM 动态生成目标类的子类,再在子类中重写目标方法,插入 MethodInterceptor.intercept() 调用。而 JVM 规范明确禁止子类覆盖 final 类或 final 方法——字节码校验器会在类加载阶段直接抛出 VerifyError 或运行时报 IllegalArgumentException: Cannot subclass final class。
常见错误现象包括:
- 代理创建时抛
java.lang.IllegalArgumentException: Cannot subclass final class xxx - 调用代理对象方法时触发
java.lang.VerifyError: Illegal inheritance from final class - Spring 启动失败,日志里出现 “CGLIB enhancement failed” 并提示 final 相关关键词
使用 Enhancer 创建代理的最小可行路径
核心是正确设置 Enhancer 实例,并传入实现 MethodInterceptor 的回调。注意:不要用反射调用原方法(即别用 method.invoke()),必须走 MethodProxy.invokeSuper() 才能绕过反射开销、触发 CGLIB 的 FastClass 优化。
实操要点:
-
enhancer.setSuperclass(TargetClass.class):目标类不能是final,否则立即失败 -
enhancer.setCallback(new MyInterceptor()):回调必须是MethodInterceptor实现,不能是InvocationHandler -
enhancer.create()返回的是子类实例,类型是TargetClass的子类,不是接口代理 - 拦截器中调用目标方法必须用
methodProxy.invokeSuper(obj, args),而非method.invoke(obj, args)
示例片段:
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service.class); // Service 非 final
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("before " + method.getName());
Object result = proxy.invokeSuper(obj, args); // 关键:走 CGLIB 快速路径
System.out.println("after " + method.getName());
return result;
});
Service proxy = (Service) enhancer.create();
proxy.publicMethod(); // ✅ 被拦截
// proxy.finalMethod(); // ❌ 编译不报错,但运行时不会进 intercept,也不会执行——因为该方法根本没被重写进代理类
如何提前发现不可代理的方法
CGLIB 不会为 final、static、private 方法生成重写逻辑,也不会报错提示——它们只是“静默跳过”。这意味着你写的 intercept() 根本不会被调用,容易误以为拦截失效。
验证方式:
- 用
javap -c反编译生成的代理类(类名类似Service$$EnhancerByCGLIB$$xxx),检查对应方法是否含invokeSuper调用 - 在
intercept()开头加日志 + 断点,确认目标方法调用时是否进入该回调 - 对关键方法显式添加单元测试:调用后检查日志/副作用是否发生
特别注意:toString()、hashCode()、equals(Object) 这些从 Object 继承的方法,默认会被 CGLIB 重写并拦截——除非你手动调用 enhancer.setUseFactory(false) 或禁用相关选项,否则它们属于“自动可拦截”范围。
兼容 JDK 代理的混合策略(避免踩坑)
如果你的代码要同时支持有接口和无接口的目标类,别强行统一用 CGLIB。更稳妥的做法是运行时判断:
- 目标类实现了接口 → 优先用
Proxy.newProxyInstance()(JDK 代理) - 目标类无接口但非
final→ 用Enhancer(CGLIB) - 目标类是
final或只有final方法 → 放弃动态代理,改用装饰器模式、编译期注解(如 AspectJ)或重构设计
Spring AOP 默认就按这个逻辑选型:@EnableAspectJAutoProxy(proxyTargetClass = false) 走 JDK,设为 true 才强制 CGLIB;但即使强制,遇到 final 类仍会 fallback 到 JDK 并报错——所以最终能否成功,还是取决于类定义本身。
最易被忽略的一点:CGLIB 生成的代理类默认没有无参构造器。如果目标类只有带参构造器,enhancer.create() 会抛 IllegalArgumentException。此时必须用 enhancer.setCallbacks() + enhancer.setConstructorArgs() 显式传参,否则代理创建直接失败。











