2 个建议在同一个函数上发生冲突

2 advices colliding on the same fucntion

嘿嘿 我开始学习 Aspectj,并且构建了 2 个方面。两个方面都有一个匹配相同功能的切入点,并且两个方面都有一个围绕该切入点执行某些操作的建议。 但是只有一个advice会被“执行”,我不明白为什么。

让我告诉你:

@Aspect
public class secondAspect {
    @Pointcut("call(  * main.*(..))")
    public void pointCutThatMatchesEveryFunctionFromMain(){

    }
    @Around("pointCutThatMatchesEveryFunctionFromMain()")
    public void adviceOnPointCutThatMatchesEveryFunctionFromMain(JoinPoint jp){
        System.out.println("A method was called");
        System.out.println(jp.getTarget().toString());
    }
}

这是第二个方面

@Aspect
public class firstAspect {


    @Pointcut("call( public * main.*(..)) && args(x,y)")
    public void pointCutOnMainClassIntArgs(int x, int y) {

    }

    @Pointcut("call( public * main.*(..)) && args(x,y)")
    public void pointCutOnMainClassFloatArgs(float x, float y) {

    }


    @Around("pointCutOnMainClassIntArgs(x,y)")
    public void doSomethingOnThisPointCut(JoinPoint pjp, int x, int y) {
        System.out.println(String.format("The method name is %s and was called with parameteres %d %d", pjp.getTarget().toString(), x, y));
    }


    @Around("pointCutOnMainClassFloatArgs(x,y)")
    public void doSomethingOnThisPointCutWithFloatArgs(JoinPoint pjp, float x, float y) {
        System.out.println(String.format("The method name is %s and was called with parameteres %f %f", pjp.getTarget().toString(), x, y));
    }


}

第一方面


public class main {
    public static void main(String[] args) {
        main maine = new main();
        maine.calculate(2,3);
        maine.calculate(2.0f,5.0f);
    }
    public void calculate(int x, int y){
        System.out.println(x+y);
    }
    public void calculate(float x, float y){
        System.out.println(x+y);
    }

}

这是我要修改的class。 只执行第二个方面的建议。我不明白为什么。

简短的回答是: 将您的建议类型从 @Around 更改为 @Before,然后它会起作用,控制台输出将变为:

A method was called
main@5f341870
The method name is main@5f341870 and was called with parameteres 2 3
The method name is main@5f341870 and was called with parameteres 2,000000 3,000000
5
A method was called
main@5f341870
The method name is main@5f341870 and was called with parameteres 2,000000 5,000000
7.0

警告: 如果您检查上面的日志输出,您会注意到您的建议 doSomethingOnThisPointCutWithFloatArgs 也与带有 int 参数的方法相匹配,这可能不是你的意图。您需要比 main.*(..) 更精确,并且在切入点中更好地使用 main.*(int, int) 而不是 main.*(float, float)

长答案是:您应该阅读 AspectJ 教程。例如,一个 @Around 建议需要

  • 一个ProoceedingJoinPoint方法参数而不是简单的JoinPoint,
  • 显式调用连接点的 proceed() 方法,以便实际调用拦截的目标方法。你没有那样做,这就是只执行了一个随机方面(AspectJ 发现的第一个方面),而不是第二个方面的解释,因为你设计建议代码的方式从未继续执行目标方法。
  • 到 return Object 或更具体的非 void 类型,如果你想匹配 return 不同于 void 的方法。您要么需要 return proceed() 的结果,要么需要 return 作为目标方法结果的其他内容。

更多建议:

  • 请遵循 Java 编码指南,不要以 class 或 lower-case 字母开头。
  • 特别不要使用 class 名称 main,然后使用方法 main,并且由于命名冲突而使用局部变量 maine。我想没有比这更丑的了。
  • 当你打印像 System.out.println(jp.getTarget().toString()) 这样的对象时,toString() 是多余的,因为在打印对象时总是隐式调用此方法。
  • 当您有多个方面以相同的连接点为目标并希望强制执行特定的调用顺序时,了解如何使用 @DeclarePrecedence
  • 我建议摆脱单独的 @Pointcut 定义并内联定义切入点,除非您希望 re-use 切入点。
  • 如果您想知道 AspectJ 中发生了什么,为什么不打印完整的连接点而不是“调用了一个方法”?它有助于理解正在发生的巨大变化。
  • 了解 call()execution() 之间的语义差异:前者拦截所有调用者(即方法调用的来源),后者拦截调用本身,无论它们来自何处。有关详细信息,请参阅 here 和 AspectJ 手册。
  • 尽量避免将您的 classes 和 aspects 放入默认包中。这不是好的风格。您还会注意到 AspectJ 切入点对包名称非常敏感。
  • 不要组合 PrintStream.printlnString.format,只需使用 PrintStream.printf.

这个怎么样?

package de.scrum_master.app;

public class Application {
  public static void main(String[] args) {
    Application application = new Application();
    application.calculate(2, 3);
    application.calculate(2.0f, 5.0f);
  }

  public void calculate(int x, int y) {
    System.out.println(x + y);
  }

  public void calculate(float x, float y) {
    System.out.println(x + y);
  }
}
package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class FirstAspect {
  @Before("execution(public !static * *(int, int)) && args(x, y) && target(targetInstance)")
  public void publicNonStaticMethodsWith2IntParameters(JoinPoint joinPoint, int x, int y, Object targetInstance) {
    System.out.printf("[FA] %s -> %s [%s, %s]%n", joinPoint, targetInstance, x, y);
  }

  @Before("execution(public !static * *(float, float)) && args(x, y) && target(targetInstance)")
  public void publicNonStaticMethodsWith2FloatParameters(JoinPoint joinPoint, float x, float y, Object targetInstance) {
    System.out.printf("[FA] %s -> %s [%s, %s]%n", joinPoint, targetInstance, x, y);
  }
}
package de.scrum_master.aspect;
import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class SecondAspect {
  @Before("execution(public !static * *(..)) && target(targetInstance)")
  public void publicNonStaticMethods(JoinPoint joinPoint, Object targetInstance) {
    System.out.printf("[SA] %s -> %s %s%n", joinPoint, targetInstance, Arrays.deepToString(joinPoint.getArgs()));
  }
}
[FA] execution(void de.scrum_master.app.Application.calculate(int, int)) -> de.scrum_master.app.Application@6c3708b3 [2, 3]
[SA] execution(void de.scrum_master.app.Application.calculate(int, int)) -> de.scrum_master.app.Application@6c3708b3 [2, 3]
5
[SA] execution(void de.scrum_master.app.Application.calculate(float, float)) -> de.scrum_master.app.Application@6c3708b3 [2.0, 5.0]
[FA] execution(void de.scrum_master.app.Application.calculate(float, float)) -> de.scrum_master.app.Application@6c3708b3 [2.0, 5.0]
7.0