AspectJ - 为什么 cflow 在不与 &&(aspectJ 交集)结合时会导致无限递归?

AspectJ - Why cflow leads to an infinite recursion when it is not combined with an && (aspectJ intersection)?

我对 AspectJ 中的 cflowcflowbelow 有疑问。

cflow(Pointcut)

Picks out all join points in the control flow of the join points picked out by the pointcut, including pointcut's join points themselves.

cflowbelow(Pointcut)

Picks out all join points in the control flow below the join points picked out by the pointcut.

对于两者,我只能找到定义和那个:

When defining pointcut using cflow or cflowbelow, we need to ensure that the pointcut does not capture the calls that are made from the same aspect, otherwise it will invoke recursive method calls and we will get WhosebugError.

This happens because cflow() will also capture the method calls from the aspect itself and try to apply the advice to it. This can be avoided by using within() construct. within() takes a type(class or interface) name as argument and captures all join points the are defined in that type.

但是没有解释 cflow() 或 cflowbelow() 在没有 within 或与 && 表达式一起使用时实际上如何导致无限递归:

pointcut aPointcut(): execution(void Test.foo()) && !cflowbelow(execution(void Test.foo()));

哪个将匹配例如Test.foo() 的第一次执行只忽略任何冒泡如果在 Test.foo() 中另一个调用 Test.foo() 或调用 class 的 foo() 方法扩展Test制作完成。

我的问题是:为什么 cflow 在使用时实际上会导致无限递归,例如没有里面? cflow的编织是如何发生的,从而导致这样的递归?

一个简单的例子:

驱动申请:

package de.scrum_master.app;

public class Application {
    public static void sayHelloTo(String name) {
        System.out.println("Hello " + name + "!");
    }

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

看点:

package de.scrum_master.aspect;

import de.scrum_master.app.Application;

public aspect CflowRecursionDemo {
    // Attention, WhosebugError!
    /*
    before() : cflow(execution(* Application.sayHelloTo(..))) {
        System.out.println(thisJoinPoint);
    }
    */

    before() : !within(CflowRecursionDemo) && cflow(execution(* Application.sayHelloTo(..))) {
        System.out.println(thisJoinPoint);
    }
}

注释掉的建议会导致 WhosebugError 而活动的建议不会。

控制台输出:

execution(void de.scrum_master.app.Application.sayHelloTo(String))
get(PrintStream java.lang.System.out)
call(java.lang.StringBuilder(String))
call(StringBuilder java.lang.StringBuilder.append(String))
call(StringBuilder java.lang.StringBuilder.append(String))
call(String java.lang.StringBuilder.toString())
call(void java.io.PrintStream.println(String))
Hello world!

说明:如您所见,在活动建议的控制流中有许多连接点。这些中的每一个都会再次触发活动建议,因为它们中的每一个都再次匹配切入点 cflow(execution(* Application.sayHelloTo(..)))。 advice 是一种与其他任何方法一样的方法,但它恰好位于一个方面的内部。无论如何,它在它自己的切入点的控制流中再次触发通知,而通知又在它自己的切入点的控制流中等等。无限递归!