如何运用CGLIB代理模式对非接口类进行业务方法增强及横向切面改写?

2026-04-27 19:251阅读0评论SEO资源
  • 内容介绍
  • 相关推荐

本文共计1010个文字,预计阅读时间需要5分钟。

如何运用CGLIB代理模式对非接口类进行业务方法增强及横向切面改写?

直接输出结果:

为什么 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+ 需要 asm 9.x 兼容 JDK 17+
  • 代理对象调用方法没触发拦截 → 检查是否用了 method.invoke() 而非 methodProxy.invokeSuper(),或者 callback 没正确 set

真正难的从来不是写几行代理代码,而是搞清「谁在调用」「谁被代理」「谁在拦截」三层关系,以及 Spring 在中间悄悄做了哪些自动决策。一旦混淆,日志里连拦截器都没进,就只能翻 Enhancercreate() 返回值类型和实际运行时类名了。

本文共计1010个文字,预计阅读时间需要5分钟。

如何运用CGLIB代理模式对非接口类进行业务方法增强及横向切面改写?

直接输出结果:

为什么 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+ 需要 asm 9.x 兼容 JDK 17+
  • 代理对象调用方法没触发拦截 → 检查是否用了 method.invoke() 而非 methodProxy.invokeSuper(),或者 callback 没正确 set

真正难的从来不是写几行代理代码,而是搞清「谁在调用」「谁被代理」「谁在拦截」三层关系,以及 Spring 在中间悄悄做了哪些自动决策。一旦混淆,日志里连拦截器都没进,就只能翻 Enhancercreate() 返回值类型和实际运行时类名了。