切面查找运行时执行的所有切入点

Aspect to find all the pointcuts executed at runtime

我需要编写一个方面(我们称之为 A)来显示运行时所有已执行的切入点。有没有一种方法可以编写像 call(...) 这样的切入点,它直接指向另一个切入点而不使用方面的名称?

我写了一些东西,主要使用对通用函数和 within() 的调用,这样当一个方面中的函数被调用时,我的方面 A 会打印一些东西。我觉得这不是一个理想的解决方案,因为我总是需要写下所有方面的名称,而且很多方面会很长。

public class Main {
    public static void main(String[] args) {
        ClassA a = new ClassA();
        a.methodA();
        a.methodA();
        a.methodB();
        a.methodA();
    }
}

public class ClassA {
    public void methodA() {
            System.out.println("MethodA");
    }
    public void methodB() {
        System.out.println("MethodB");
    }
    public void methodC() {
        System.out.println("MethodC");
    }
}

public aspect MethodAAspect {
    pointcut MethA():
        call(public * ClassA.methodA());
    pointcut MethC():
        call(public * ClassA.methodC());
    after():
        MethA() {
            System.out.println("Aspect here, methodA ended.");
        }
    after():
        MethC() {
            System.out.println("Aspect here, methodC ended.");
        }       
}

如果在此示例中,我需要一个方面来计算所有切入点已执行的次数或在执行切入点时打印一些内容,我应该如何编写?

我不确定我是否正确理解了您想要实现的目标,因为您的描述有些不准确。例如,切入点不是 "executed",应用程序方法或切面内的建议是。所以我不确定您是否只想记录每个方法调用(或者可能是方法执行,请参阅下面的差异)或者可能需要某种元方面来计算方面建议被执行的次数。我在这里假设前一种更简单的情况,因为它对我来说最有意义。

您的应用程序代码,但包含包名称:

package de.scrum_master.app;

public class ClassA {
  public void methodA() {
    System.out.println("MethodA");
  }

  public void methodB() {
    System.out.println("MethodB");
  }

  public void methodC() {
    System.out.println("MethodC");
  }
}
package de.scrum_master.app;

public class Main {
  public static void main(String[] args) {
    ClassA a = new ClassA();
    a.methodA();
    a.methodA();
    a.methodB();
    a.methodA();
  }
}

切面拦截方法执行:

此方面拦截您自己代码中的所有方法执行(不是调用!)。

package de.scrum_master.aspect;

public aspect MyAspect {
  after() : execution(* *(..)) {
    System.out.println(thisJoinPoint);
  }
}

日志输出为:

MethodA
execution(void de.scrum_master.app.ClassA.methodA())
MethodA
execution(void de.scrum_master.app.ClassA.methodA())
MethodB
execution(void de.scrum_master.app.ClassA.methodB())
MethodA
execution(void de.scrum_master.app.ClassA.methodA())
execution(void de.scrum_master.app.Main.main(String[]))

请注意,Main.main(..) 的执行也会被记录,即使从未显式调用该方法(但仍然执行!)。

切面拦截方法执行:

此方面拦截所有方法从您自己的代码调用,其中还包括对第三方或JDK 类.[=25= 的调用]

请注意,为了避免死循环,我们必须添加 && !within(MyAspect),因为方面建议还调用了 JDK 方法。

package de.scrum_master.aspect;

public aspect MyAspect {
  after() : call(* *(..)) && !within(MyAspect) {
    System.out.println(thisJoinPoint);
  }
}

本例日志输出为:

MethodA
call(void java.io.PrintStream.println(String))
call(void de.scrum_master.app.ClassA.methodA())
MethodA
call(void java.io.PrintStream.println(String))
call(void de.scrum_master.app.ClassA.methodA())
MethodB
call(void java.io.PrintStream.println(String))
call(void de.scrum_master.app.ClassA.methodB())
MethodA
call(void java.io.PrintStream.println(String))
call(void de.scrum_master.app.ClassA.methodA())

当然你也可以使用call()并限制调用你自己的包,例如:

package de.scrum_master.aspect;

public aspect MyAspect {
  after() : call(* de.scrum_master.app..*(..)) && !within(MyAspect) {
    System.out.println(thisJoinPoint);
  }
}

这里的日志输出是:

MethodA
call(void de.scrum_master.app.ClassA.methodA())
MethodA
call(void de.scrum_master.app.ClassA.methodA())
MethodB
call(void de.scrum_master.app.ClassA.methodB())
MethodA
call(void de.scrum_master.app.ClassA.methodA())

这总是取决于你想要达到什么。

如果这不是您想要的,请更准确地更新您的问题并通过评论通知我。然后我看看我能做什么。


有关元方面后续问题的更新:

package de.scrum_master.aspect;

public aspect MetaAspect {
  before() : adviceexecution() && !within(MetaAspect) {
    System.out.println(thisJoinPoint);
  }
}

使用 execution() 的第一个 MyAspect 版本的日志更改为:

MethodA
adviceexecution(void de.scrum_master.aspect.MyAspect.after(JoinPoint))
execution(void de.scrum_master.app.ClassA.methodA())
MethodA
adviceexecution(void de.scrum_master.aspect.MyAspect.after(JoinPoint))
execution(void de.scrum_master.app.ClassA.methodA())
MethodB
adviceexecution(void de.scrum_master.aspect.MyAspect.after(JoinPoint))
execution(void de.scrum_master.app.ClassA.methodB())
MethodA
adviceexecution(void de.scrum_master.aspect.MyAspect.after(JoinPoint))
execution(void de.scrum_master.app.ClassA.methodA())
adviceexecution(void de.scrum_master.aspect.MyAspect.after(JoinPoint))
execution(void de.scrum_master.app.Main.main(String[]))