AspectJ环绕通知调用目标方法:避免无限循环且不遗漏方法捕获
AspectJ环绕通知调用目标方法避免无限循环的解决方案
我们需要用AspectJ捕获com.ABC.MyClass中所有方法的执行,目标类和初始切面代码如下:
目标类代码:
package com.ABC; public class MyClass{ public void methodA(){ } public void methodB(){ methodA(); } }
初始切面代码:
@Aspect public class MyAspect{ @Pointcut("execution(* com.ABC.MyClass.*(..))") public void captureMyClassMethods(){ } @Around("captureMyClassMethods()") public Object aroundMyClassMethods(ProceedingJoinPoint jp) throws Throwable{ MyClass object = (MyClass)jp.getTarget(); object.methodA(); // 此处引发无限循环 Object o = jp.proceed(); System.out.println("Some data from o"); return o; } }
问题说明:
- 环绕通知中调用
object.methodA()会触发切点匹配,导致无限循环。 - 若改用
execution(* com.ABC.MyClass.*(..)) && !cflow(within(MyAspect))作为切点,会遗漏methodB()内部调用的methodA(),因为该调用处于切面的控制流中。
方案一:使用@Privileged注解(AspectJ原生支持)
通过@Privileged标记环绕通知,让通知内部发起的目标方法调用不触发当前切面的切点,同时不影响目标类内部方法调用的捕获。
修改后的切面代码:
@Aspect public class MyAspect{ @Pointcut("execution(* com.ABC.MyClass.*(..))") public void captureMyClassMethods(){ } @Privileged @Around("captureMyClassMethods()") public Object aroundMyClassMethods(ProceedingJoinPoint jp) throws Throwable{ MyClass object = (MyClass)jp.getTarget(); object.methodA(); // 不会引发无限循环 Object o = jp.proceed(); System.out.println("Some data from o"); return o; } }
原理:@Privileged是AspectJ的原生注解,它会让通知内部的方法调用跳过当前切面的切点匹配。这样:
- 通知内直接调用
object.methodA()时,不会触发当前切面的环绕通知,避免无限循环。 methodB()内部调用的methodA()属于目标类自身的执行流程,不在@Privileged的作用范围内,仍会被正常捕获。
方案二:通过反射直接调用目标方法
利用Java反射绕开AspectJ织入的切面代码,直接调用目标方法的原始实现。
修改后的切面代码:
import java.lang.reflect.Method; @Aspect public class MyAspect{ @Pointcut("execution(* com.ABC.MyClass.*(..))") public void captureMyClassMethods(){ } @Around("captureMyClassMethods()") public Object aroundMyClassMethods(ProceedingJoinPoint jp) throws Throwable{ MyClass object = (MyClass)jp.getTarget(); // 通过反射调用methodA,绕开切面 Method methodA = MyClass.class.getDeclaredMethod("methodA"); methodA.setAccessible(true); methodA.invoke(object); Object o = jp.proceed(); System.out.println("Some data from o"); return o; } }
原理:
反射调用直接操作目标方法的字节码实现,不会触发AspectJ织入的切面逻辑,因此不会产生无限循环。同时,methodB()内部的methodA()调用仍会被切面正常捕获。
内容的提问来源于stack exchange,提问作者komal Thawani
相关产品推荐
相关产品推荐

