Aspectj - 如何在同一个建议中调用建议方法,而不会触发无限循环

时间:2014-10-29 21:51:00

标签: java aspectj infinite-loop pointcut

我想使用AspectJ记录我在代码中进行的所有方法调用,除了记录器中的方法之外。

@Aspect
public class Logger
{
    // Point Cuts
    //-----------
    @Pointcut("execution(* org.myDomain.*..*.*(..))")
    public void selectAll(){}

    @Pointcut("within(Logger) && call(* *(..))")
    public void codeWithinAspect(){}

    // Advices
    //-----------
    @Before("selectAll()")
    public void adviceThatWorksFine(JoinPoint joinPoint)
    {
        System.out.print(joinPoint.getSignature().toString());
        //Utils.printToConsole(joinPoint.getSignature().toString());    
    }

    @Before("selectAll() && !codeWithinAspect")
    public void adviceWithInfiniteLoop(JoinPoint joinPoint)
    {
        //System.out.print(joinPoint.getSignature().toString());
        Utils.printToConsole(joinPoint.getSignature().toString());  
    }
}

该类中的第一个建议工作正常(它将每个方法调用写入控制台),第二个建议在调用org.myDomain.utils.Utils.printToConsole()方法时导致无限循环,这是通过调用建议的建议。

我发现这是链接中描述的常见问题 http://www.eclipse.org/aspectj/doc/released/faq.php#q:infiniterecursion 但我无法理解如何编写切入点,因此无法创建无限循环。

帮助

1 个答案:

答案 0 :(得分:2)

您的代码中存在几个问题:

  • !codeWithinAspect需要括号:!codeWithinAspect()
  • adviceWithInfiniteLoop()以这种方式结合了execution()call()切入点:execution(foo) && !call(bar)。因为调用连接点永远不能是执行连接点,所以条件的第二部分始终为true且无效。因此,它无法避免无限循环。
  • 您不仅要在方面Logger中排除连接点,还要排除该方面方法的控制流(cflow())内的连接点,即由它们直接或间接调用的内容。

解决方案如下:

日志输出的实用程序类:

package org.myDomain.app;

public class Utils {
    public static void printToConsole(Object object) {
        System.out.println(object);
    }
}

驱动程序应用程序:

package org.myDomain.app;

public class Application {
    public  static void sayHelloTo(String counterpart) {
        Utils.printToConsole("Hello " + counterpart + "!");
    }

    public static void main(String[] args) {
        sayHelloTo("world");
    }
}

记录器方面:

package org.myDomain.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.myDomain.app.Utils;

@Aspect
public class Logger {
    @Pointcut("execution(* org.myDomain..*(..))")
    public void selectAll() {}

    @Pointcut("cflow(within(Logger))")
    public void codeWithinAspect() {}

    @Before("selectAll() && !codeWithinAspect()")
    public void advice(JoinPoint joinPoint) {
        Utils.printToConsole(joinPoint);
    }
}

控制台输出:

execution(void org.myDomain.app.Application.main(String[]))
execution(void org.myDomain.app.Application.sayHelloTo(String))
execution(void org.myDomain.app.Utils.printToConsole(Object))
Hello world!

享受!

更新:如果要排除所有建议执行控制流,您也可以使用此切入点:

@Pointcut("cflow(adviceexecution())")
public void codeWithinAspect() {}