如何运用CGLIB代理模式对非接口类进行业务方法增强及横向切面改写?
- 内容介绍
- 相关推荐
本文共计1010个文字,预计阅读时间需要5分钟。
直接输出结果:
为什么 CGLIB 能代理没有接口的类
CGLIB 不靠接口,而是靠字节码生成——它在运行时动态创建目标类的子类,把非 final 方法全部重写一遍,在方法入口/出口插入拦截逻辑。这和 JDK 动态代理“实现接口+反射调用”有本质区别。
所以只要目标类允许被继承(非 final)、方法允许被覆写(非 final / static),就能被增强。
-
Enhancer是核心工厂类,负责设置父类、回调、命名策略等 -
MethodInterceptor.intercept()是唯一要实现的方法,它接收原方法调用上下文,并决定是否、何时、如何调用原始方法(通过methodProxy.invokeSuper()) - 注意:
method.invoke(target, args)是反射调用,性能差;methodProxy.invokeSuper(proxy, args)是 FastClass 优化调用,必须用这个
怎么写一个最简可用的 CGLIB 代理增强
不依赖 Spring,纯 CGLIB 原生用法,三步到位:
- 定义目标类(比如
User),确保它不是final,方法也不是final - 实现
MethodInterceptor,在intercept()中织入前置/后置逻辑,并用methodProxy.invokeSuper(proxy, args)调用原方法 - 用
Enhancer设置setSuperclass(User.class)和setCallback(你的拦截器),然后create()
示例关键片段:
public class LogInterceptor implements MethodInterceptor { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("【前置】" + method.getName()); Object result = methodProxy.invokeSuper(proxy, args); // ⚠️不是 method.invoke() System.out.println("【后置】" + method.getName()); return result; } } // 使用 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(User.class); enhancer.setCallback(new LogInterceptor()); User proxy = (User) enhancer.create();
CGLIB 代理在 Spring AOP 中自动切换的坑
Spring AOP 默认优先用 JDK 动态代理,只有当目标对象没实现任何接口时,才 fallback 到 CGLIB。但这个“自动”容易误判:
- 如果目标类实现了接口,哪怕你只打算增强它的某个非接口方法,Spring 仍会走 JDK 代理 → 那些没在接口里声明的方法根本不会被拦截
- 想强制启用 CGLIB,得在配置中加
@EnableAspectJAutoProxy(proxyTargetClass = true)(注解方式)或<aop:config proxy-target-class="true"/>(XML) - Spring Boot 2.5+ 默认已设为
true,但老项目或手动配ProxyFactoryBean时仍可能漏掉 - 开启 CGLIB 后,
final方法依然无法被代理 —— 这不是配置问题,是技术限制,别试图绕过
常见报错和对应排查点
遇到代理失败,先看错误信息里有没有这些关键词:
-
java.lang.IllegalArgumentException: Cannot subclass final class→ 目标类或方法是final,删掉final或换设计 -
No suitable constructor found→ 目标类没有无参构造器,CGLIB 子类实例化失败;要么加无参构造,要么用setConstructorArgs()传参 -
java.lang.NoClassDefFoundError: org/objectweb/asm/Type→ 缺少asm依赖,CGLIB 3.3+ 需要asm9.x 兼容 JDK 17+ - 代理对象调用方法没触发拦截 → 检查是否用了
method.invoke()而非methodProxy.invokeSuper(),或者callback没正确 set
真正难的从来不是写几行代理代码,而是搞清「谁在调用」「谁被代理」「谁在拦截」三层关系,以及 Spring 在中间悄悄做了哪些自动决策。一旦混淆,日志里连拦截器都没进,就只能翻 Enhancer 的 create() 返回值类型和实际运行时类名了。
本文共计1010个文字,预计阅读时间需要5分钟。
直接输出结果:
为什么 CGLIB 能代理没有接口的类
CGLIB 不靠接口,而是靠字节码生成——它在运行时动态创建目标类的子类,把非 final 方法全部重写一遍,在方法入口/出口插入拦截逻辑。这和 JDK 动态代理“实现接口+反射调用”有本质区别。
所以只要目标类允许被继承(非 final)、方法允许被覆写(非 final / static),就能被增强。
-
Enhancer是核心工厂类,负责设置父类、回调、命名策略等 -
MethodInterceptor.intercept()是唯一要实现的方法,它接收原方法调用上下文,并决定是否、何时、如何调用原始方法(通过methodProxy.invokeSuper()) - 注意:
method.invoke(target, args)是反射调用,性能差;methodProxy.invokeSuper(proxy, args)是 FastClass 优化调用,必须用这个
怎么写一个最简可用的 CGLIB 代理增强
不依赖 Spring,纯 CGLIB 原生用法,三步到位:
- 定义目标类(比如
User),确保它不是final,方法也不是final - 实现
MethodInterceptor,在intercept()中织入前置/后置逻辑,并用methodProxy.invokeSuper(proxy, args)调用原方法 - 用
Enhancer设置setSuperclass(User.class)和setCallback(你的拦截器),然后create()
示例关键片段:
public class LogInterceptor implements MethodInterceptor { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("【前置】" + method.getName()); Object result = methodProxy.invokeSuper(proxy, args); // ⚠️不是 method.invoke() System.out.println("【后置】" + method.getName()); return result; } } // 使用 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(User.class); enhancer.setCallback(new LogInterceptor()); User proxy = (User) enhancer.create();
CGLIB 代理在 Spring AOP 中自动切换的坑
Spring AOP 默认优先用 JDK 动态代理,只有当目标对象没实现任何接口时,才 fallback 到 CGLIB。但这个“自动”容易误判:
- 如果目标类实现了接口,哪怕你只打算增强它的某个非接口方法,Spring 仍会走 JDK 代理 → 那些没在接口里声明的方法根本不会被拦截
- 想强制启用 CGLIB,得在配置中加
@EnableAspectJAutoProxy(proxyTargetClass = true)(注解方式)或<aop:config proxy-target-class="true"/>(XML) - Spring Boot 2.5+ 默认已设为
true,但老项目或手动配ProxyFactoryBean时仍可能漏掉 - 开启 CGLIB 后,
final方法依然无法被代理 —— 这不是配置问题,是技术限制,别试图绕过
常见报错和对应排查点
遇到代理失败,先看错误信息里有没有这些关键词:
-
java.lang.IllegalArgumentException: Cannot subclass final class→ 目标类或方法是final,删掉final或换设计 -
No suitable constructor found→ 目标类没有无参构造器,CGLIB 子类实例化失败;要么加无参构造,要么用setConstructorArgs()传参 -
java.lang.NoClassDefFoundError: org/objectweb/asm/Type→ 缺少asm依赖,CGLIB 3.3+ 需要asm9.x 兼容 JDK 17+ - 代理对象调用方法没触发拦截 → 检查是否用了
method.invoke()而非methodProxy.invokeSuper(),或者callback没正确 set
真正难的从来不是写几行代理代码,而是搞清「谁在调用」「谁被代理」「谁在拦截」三层关系,以及 Spring 在中间悄悄做了哪些自动决策。一旦混淆,日志里连拦截器都没进,就只能翻 Enhancer 的 create() 返回值类型和实际运行时类名了。

