Spring AOP中重载方法嵌套调用时Pointcut拦截行为的问询
为什么AOP拦截行为在重载与非重载嵌套调用中不一样?
场景1:重载方法嵌套调用(两次输出)
先看你的业务与AOP代码:
// 业务方法 public void sample(int a) { sample(a, 5); } public void sample(int a, int offset) { System.out.println(a + offset); } // AOP切点与通知 @Pointcut("execution(public * sample*(..))") public void sampleMethod() {} @Around("sampleMethod()") public Object storeMetrics(final ProceedingJoinPoint jp) throws Throwable { System.out.println("Advice called"); return jp.proceed(); // 注:原代码未写此句,但要触发后续方法执行必须添加,否则第二个重载方法不会被调用 }
当调用sample(10)时两次输出"Advice called",确实是因为两个重载方法都被Pointcut拦截了,背后是Spring AOP的代理逻辑在起作用:
- 外部调用
sample(10)时,你调用的是Spring生成的代理对象的方法,自然触发AOP拦截,输出第一次日志。 - 内部调用
sample(a,5)时,由于这是同一个类下的重载方法,Spring的代理类会为所有匹配Pointcut的方法生成代理逻辑(也就是两个sample方法都被代理覆盖了),所以这次内部调用其实还是走的代理方法,因此再次触发AOP拦截,输出第二次日志。
场景2:非重载方法嵌套调用(一次输出)
再看这个场景的代码:
// 业务方法 public void sample(int a) { sampleWithOffset(a, 5); } public void sampleWithOffset(int a, int offset) { System.out.println(a + offset); } // AOP定义和上面一致 @Pointcut("execution(public * sample*(..))") public void sampleMethod() {} @Around("sampleMethod()") public Object storeMetrics(final ProceedingJoinPoint jp) throws Throwable { System.out.println("Advice called"); return jp.proceed(); }
这里调用sample(10)只输出一次日志,核心原因是Spring AOP管不到类内部的this调用:
- 外部调用
sample(10)时,走的是代理对象,所以被AOP拦截,输出一次日志。 - 但在
sample(int a)内部调用sampleWithOffset(a,5)时,是通过this引用直接调用原始类的方法,完全绕开了代理对象。Spring AOP的动态代理只能拦截通过代理对象发起的调用,这种内部自调用不会触发代理逻辑,所以哪怕sampleWithOffset匹配Pointcut,也不会被AOP拦截。
补充:怎么让内部调用也被拦截?
如果想让非重载的内部方法调用也触发AOP,有两种常见方案:
- 用
AopContext.currentProxy()获取代理对象,然后通过代理对象调用方法,比如把内部调用改成((YourClass)AopContext.currentProxy()).sampleWithOffset(a,5)(注意要在类上添加@EnableAspectJAutoProxy(exposeProxy = true)开启代理暴露)。 - 换用AspectJ编译时或加载时织入,AspectJ是直接修改字节码,不管是外部还是内部调用都能拦截,不受Spring动态代理的限制。
内容的提问来源于stack exchange,提问作者Sunil
相关产品推荐
相关产品推荐

