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

huangapple go评论167阅读模式
英文:

2 advices colliding on the same fucntion

问题

嘿,

我开始学习 Aspectj,并且我已经建立了两个切面。这两个切面都有一个切点,匹配同一个函数,而且两个切面都有一个环绕通知,在这个切点上执行某些操作。
然而,只有一个通知会被“执行”,我不明白为什么。

让我给你展示:

第二个切面:

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

    }
    @Around("pointCutThatMatchesEveryFunctionFromMain()")
    public void adviceOnPointCutThatMatchesEveryFunctionFromMain(JoinPoint jp){
        System.out.println("调用了一个方法");
        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("方法名是 %s,参数为 %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("方法名是 %s,参数为 %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);
    }
}

只有 secondAspect 的通知被执行。我不明白为什么。

英文:

Hei,
I started learning Aspectj and I have built 2 aspects. Both aspects have a pointcut that match the same function, and both aspects have an around advice that will do something on that pointcut.
However only one advice will be "executed", and I do not understand why.

let me show you:

@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());
    }
}

that is the second aspect

@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));
    }


}

the first aspect


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);
    }

}

and this is the class that i want to modify.
Only the secondAspect's adivce is executed. And I don't get it why.

答案1

得分: 1

短答案是: 将你的建议类型从 @Around 更改为 @Before,然后它就会生效,控制台输出将变为:

方法已调用
main@5f341870
方法名称是 main@5f341870,并且使用参数 2 3 进行了调用
方法名称是 main@5f341870,并且使用参数 2,000000 3,000000 进行了调用
5
方法已调用
main@5f341870
方法名称是 main@5f341870,并且使用参数 2,000000 5,000000 进行了调用
7.0

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

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

  • 一个 ProceedingJoinPoint 方法参数,而不是简单的 JoinPoint
  • 显式调用连接点的 proceed() 方法,以实际调用拦截的目标方法。你没有这样做,这就是为什么只有一个随机方面(由 AspectJ 找到的第一个)被执行,而第二个则没有,因为你设计的建议代码从未继续执行到目标方法。
  • 返回 Object 或更具体的非 void 类型,如果你想匹配返回值不是 void 的方法。你需要返回 proceed() 的结果或你希望作为目标方法结果返回的其他内容。

一些建议:

  • 请遵循 Java 编码准则,不要以小写字母开头命名类或切面。
  • 尤其不要使用类名 main,然后是方法 main,由于命名冲突还有一个局部变量 maine。我想这再丑陋不过了。
  • 当你打印一个对象,比如 System.out.println(jp.getTarget().toString())toString() 是多余的,因为在打印对象时,这个方法将总是被隐式调用。
  • 当你有多个切面定位相同的连接点并希望强制按特定顺序调用它们时,学习如何使用 @DeclarePrecedence
  • 我建议摆脱单独的 @Pointcut 定义,并在内联中定义你的切入点,除非你想重用一个切入点。
  • 如果你想知道在 AspectJ 中发生了什么,为什么不打印出完整的连接点,而不是只打印“方法已调用”?这有助于极大地理解发生了什么。
  • 学习 call()execution() 之间的语义区别:前者拦截所有调用者(即方法调用的源),后者无论调用从何处发起,都拦截调用本身。在这里查看更多信息:这里,以及 AspectJ 手册中的更多信息。
  • 尽量避免将你的类和切面放入默认包中。这不是良好的风格。此外,你会注意到 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

<details>
<summary>英文:</summary>

**The short answer is:** Change your advice type from `@Around` to `@Before`, then it works and the console output will turn into:

```none
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

Caveat: If you inspect the above log output, you will notice that your advice doSomethingOnThisPointCutWithFloatArgs also matches the method with the int parameters, which probably was not your intention. You need to be more precise than main.*(..) and better use main.*(int, int) vs. main.*(float, float) in your pointcuts.

The long answer is: You should read an AspectJ tutorial. For example, an @Around advice needs

  • a ProoceedingJoinPoint method parameter instead of a simple JoinPoint,
  • to explicitly call the joinpoint's proceed() method in order to actually call the intercepted target method. You did not do that, which is the explanation which only one random aspect (the first one found by AspectJ) was executed, but not the second because you designed your advice code in such a way that you never proceeded to the target method.
  • to return Object or a more specific non-void type if you want to match methods returning something other than void. You either need to return the result of proceed() or something else you wish to be returned as the result of the target method.

Some more suggestions:

  • Please follow Java coding guidelines and do not start class or aspect names with lower-case letters.
  • Specifically do not use a class name main, then a method main and due to the naming clash a local variable maine. I guess it does not get much uglier than that.
  • When you print an object like System.out.println(jp.getTarget().toString()), the toString() is superfluous because this method will always be called implicitly when printing objects.
  • When you have multiple aspects targeting the same joinpoints and wish to enforce a specific order in which they are called, learn how to use @DeclarePrecedence.
  • I recommend to get rid of separate @Pointcut definitions and define your pointcuts inline unless you wish to re-use a pointcut.
  • If you want to know what is going on in AspectJ, why not print the full joinpoint instead of "a method was called"? It helps understand what is going on tremendously.
  • Learn the semantic difference between call() and execution(): While the former intercepts all callers (i.e. the sources of method calls), the latter intercepts the calls themselves no matter where they originate from. See here and the AspectJ manual for more information.
  • Try to avoid putting your classes and aspects into the default package. This is not good style. Also you will notice that AspectJ pointcuts can be quite sensitive to package names.
  • Instead of combining PrintStream.println and String.format, just use PrintStream.printf.

How about this?

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(&quot;execution(public !static * *(int, int)) &amp;&amp; args(x, y) &amp;&amp; target(targetInstance)&quot;)
  public void publicNonStaticMethodsWith2IntParameters(JoinPoint joinPoint, int x, int y, Object targetInstance) {
    System.out.printf(&quot;[FA] %s -&gt; %s [%s, %s]%n&quot;, joinPoint, targetInstance, x, y);
  }

  @Before(&quot;execution(public !static * *(float, float)) &amp;&amp; args(x, y) &amp;&amp; target(targetInstance)&quot;)
  public void publicNonStaticMethodsWith2FloatParameters(JoinPoint joinPoint, float x, float y, Object targetInstance) {
    System.out.printf(&quot;[FA] %s -&gt; %s [%s, %s]%n&quot;, 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(&quot;execution(public !static * *(..)) &amp;&amp; target(targetInstance)&quot;)
  public void publicNonStaticMethods(JoinPoint joinPoint, Object targetInstance) {
    System.out.printf(&quot;[SA] %s -&gt; %s %s%n&quot;, joinPoint, targetInstance, Arrays.deepToString(joinPoint.getArgs()));
  }
}
[FA] execution(void de.scrum_master.app.Application.calculate(int, int)) -&gt; de.scrum_master.app.Application@6c3708b3 [2, 3]
[SA] execution(void de.scrum_master.app.Application.calculate(int, int)) -&gt; de.scrum_master.app.Application@6c3708b3 [2, 3]
5
[SA] execution(void de.scrum_master.app.Application.calculate(float, float)) -&gt; de.scrum_master.app.Application@6c3708b3 [2.0, 5.0]
[FA] execution(void de.scrum_master.app.Application.calculate(float, float)) -&gt; de.scrum_master.app.Application@6c3708b3 [2.0, 5.0]
7.0

huangapple
  • 本文由 发表于 2020年10月26日 21:06:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/64537738.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定