methodinterceptor 必须配合 enhancer 才能生效,单独实现接口无作用;enhancer 通过生成子类并设置回调来触发 intercept(),调用原方法必须用 methodproxy.invokesuper() 而非 method.invoke()。

MethodInterceptor 必须配合 Enhancer 才能生效
单独实现 MethodInterceptor 接口没有任何作用——它只是定义了拦截逻辑的签名,真正触发拦截的是 Enhancer 在运行时生成的子类。如果你只写了拦截器但没用 Enhancer 创建代理实例,方法调用完全不会经过 intercept()。
常见错误现象:System.out.println 在 intercept() 里加了,但运行时根本没输出;目标方法照常执行,增强逻辑被完全跳过。
-
Enhancer必须调用setSuperclass(Class)指定被代理的目标类(不能是接口) -
setCallback()或setCallbacks()必须传入实现了MethodInterceptor的实例 - 目标类不能是
final类,其被增强的方法也不能是final或static - 如果目标类构造函数有参数,需用
enhancer.setConstructorArgs(...)显式传入
intercept() 中必须用 methodProxy.invokeSuper() 调用原方法
很多初学者在 intercept() 里直接写 method.invoke(obj, args),这会触发反射、绕过 CGLIB 的 FastClass 优化,甚至导致栈溢出(因为 obj 是代理对象,method.invoke 又会再次进入 intercept)。
正确做法是始终使用 methodProxy.invokeSuper(obj, args)。它不走反射,而是通过 CGLIB 生成的 FastClass 查表调用父类原始方法,性能接近直接调用。
-
methodProxy是 CGLIB 为每个被重写方法生成的快速调用句柄,和method不是一回事 - 不要尝试缓存
methodProxy实例——它和代理对象生命周期绑定,跨代理复用会出错 - 若需访问原始
Method对象(比如读取注解),仍可用method参数,但调用必须走invokeSuper
非 public 方法无法被 CGLIB 拦截
CGLIB 基于继承生成子类,只能重写 public 和 protected 方法。private 方法不可见,package-private(默认访问级别)方法在子类中是否可重写,取决于子类与目标类是否在同一包——而 CGLIB 生成的代理类包名由命名策略决定,默认不在原包下,因此默认访问级别的方法基本无法拦截。
这意味着:如果你的业务类把关键逻辑放在 private void doWork() 里,再怎么写 MethodInterceptor 都无效。
- 想确保可拦截,方法至少声明为
protected - Spring AOP 默认也不拦截 package-private 方法,行为一致
- 没有“绕过访问控制”的合法方式;强行用反射调用 private 方法会破坏代理语义,且失去 FastClass 优势
Enhancer.create() 返回的是子类实例,类型强转要小心
Enhancer.create() 返回的是一个新生成的子类实例,它的运行时类型是类似 GreetingService$$EnhancerByCGLIB$$a1b2c3d4 的匿名类,不是原始类本身。虽然它继承自目标类,但直接 (GreetingService) enhancer.create() 在大多数情况下能成功,前提是目标类有无参构造函数。
容易踩的坑:
- 目标类只有带参构造函数 →
create()抛IllegalArgumentException: No default constructor - 强转时用了错误的类型(比如误转成接口)→
ClassCastException - 多个线程并发调用
create()→ CGLIB 内部有缓存,但首次生成类较慢,建议预热或复用Enhancer实例 - 代理对象持有对拦截器的强引用 → 若拦截器持有了大对象或上下文,可能引发内存泄漏
真正复杂的地方在于:CGLIB 的拦截不是“包装”,而是“重写+回调”。你写的 intercept() 逻辑,实际运行在子类方法体内,和原始方法处于同一调用栈层级——这意味着异常传播、this 引用、泛型擦除等细节都必须按继承语义理解,而不是代理模式的直觉。稍不注意,就会把子类行为当成原类行为来推理。










