在一个方面在同一个连接点上运行前,后,和后建议时,建议优先级不明确

时间:2016-10-11 13:38:18

标签: java aop aspectj

请考虑这个简单的Java代码

public class Application {

  public void m(int i) {
   System.out.println("M with argument " + i );
  }

  public static void main(String[] arg) {
   Application t = new Application();
   t.m(25);  
  }
}

我已经定义了以下Aspect来操作这个类:

public aspect Basics {
  public void output(String tag, Object o) {
    System.out.println(tag + ": " + o);
  }

  pointcut callM(int i): call(void Application.m(int)) && args(i);

  before(int i): callM(i) {
    output("before M", i);  
  }

  void around(int i): callM(i) {
      output("around-advice", i);
      proceed(1);
      output("after proceed", i);
  }

  after(int i): callM(i) {
    output("After M", i);  
  }
}

值得注意的是,around-advice会将传递给方法M的参数值更改为1。 运行此代码会生成以下输出:

before M: 25
around-advice: 25
M with argument 1
after proceed: 25
After M: 25

除了最后一行之外,整个输出都是我预期的。我希望最后一行打印'1'而不是'25'。有人可以向我解释为什么会这样吗?

在自己寻找答案的同时,我试图改变建议的顺序,但这最终只会让混乱变得更大。如果我在代码中首先提出了post-advice,然后是before-advice,然后将around-advice放在最后(即(1)之后 - (2)之前 - (3)around),我得到以下输出:

before M: 25
around-advice: 25
M with argument 1
After M: 1
after proceed: 25

对我而言,这是唯一具有完美意义的输出。

但是,如果我首先提出了建议后,然后在将建议放在最后(即(1)之后 - (2)之后 - (3)之前)的周围建议,我得到以下输出如果我把以前的订单输出考虑在内,这对我来说也没什么意义:

around-advice: 25
before M: 1
M with argument 1
After M: 1
after proceed: 25

在这种情况下,之前的建议会被'i'限制为1.我的猜测是,这是由于周围的建议首先被触发(因为排序)而且之前的建议实际上是周围建议的主体呼吁“继续”触发。但是,遵循此逻辑并不能解释在此问题中首先讨论的排序中生成的输出。

最后,以这样的方式改变排序:我们首先得到之前的建议,然后是后期建议,然后是周围建议(即(1)之前 - (2)之后 - (3)根据Eclipse的AspectJ-plugin,这是无效的,因为这会产生一个'循环建议优先'。

有人可以解释一下在同一方面的不同建议之间使用的优先级,以解释上述所有行为吗?

我一直在阅读here这个主题,但我认为解释是不确定的/与实施不符。 它说

  

一条around advice控制是否通过调用proceed来运行较低优先级的建议。对继续的调用将运行具有下一个优先级的通知,或者如果没有进一步的建议则运行在连接点下的计算。

如果我理解正确,这意味着在这个问题中首先讨论的输出(即(1)之前 - (2)约 - (3)在排序之后)应该在最后一行中有'1'而不是比'25'。

2 个答案:

答案 0 :(得分:0)

在同一方面声明的建议的默认优先级在文档中说明如下:

  

如果在同一方面定义了两条建议,则有两种情况:

     
      
  • 如果要么是在建议之后,那么在该方面中稍后出现的那个优先于先前出现的那个。
  •   
  • 否则,该方面中较早出现的那个优先于稍后出现的那个。
  •   

你需要理解的是,由于建议是用字节代码编写的,你在第一次订购时得到的结果应该是这样的:

 ---------------
| Around()      |
|   ----------  |
|  | Before() | |
|   ----------  |
|  | Call()   | |
|   ----------  |
 ---------------
| After()       |
 ---------------

i的值仅在Around的范围内更改,这就是After打印值25的原因。

在你的第二种情况下,排序给出了类似的东西:

 ---------------
| Before()      |
 ---------------
| Around()      |
|   ----------  |
|  | Call()   | |
|   ----------  |
|  | After()  | |
|   ----------  |
 ---------------

在这种情况下,值会按预期打印。

答案 1 :(得分:0)

实际上@ XGouchet的第一张草图并不完全正确,因为之前的建议不会发生在围绕的范围内,而是在围绕之前完成>被执行。让我使用伪代码表示法,用括号表示这种“词法范围”:

情景A:之前的词汇排序 - > 周围 - >后的

before(25)
around(25) {
    proceed(1)
}
after(25)

情景B:在之后的词汇排序 - > 之前 - > 围绕

before(25)
around(25) {
    proceed(1)
    after(1)
}

场景C:在之后的词汇排序 - > 周围 - >

around(25) {
    before(1)
    proceed(1)
    after(1)
}

现在还要记住方面的行为方式以及在某些情况下优先级的真正含义。同时引用AspectJ手册中有关方面优先级的章节中的Effects of precedence段落:

  

在特定的连接点,建议按优先顺序排列。

     
      
  • 一段 around 建议控制是否通过调用proceed来运行较低优先级的建议。对继续的调用将运行具有下一优先级的通知,或者如果没有进一步的建议,则运行在连接点下的计算。
  •   
  • 建议之前的一段可以通过抛出异常来阻止较低优先级的建议。但是,如果它正常返回,则将运行下一个优先级的建议,或者如果没有进一步的建议,则在连接pint下的计算。
  •   
  • 在返回建议后运行将运行下一个优先级的建议,或者如果没有进一步的建议则运行连接点下的计算。然后,如果该计算正常返回,则建议的主体将运行。
  •   
  • 在抛出建议后运行将运行下一个优先级的建议,或者如果没有进一步的建议则运行连接点下的计算。然后,如果该计算引发了适当类型的异常,则通知的主体将运行。
  •   
  • 建议之后运行将运行下一个优先级的建议,或者如果没有进一步的建议则运行连接点下的计算。然后,建议的主体将运行。
  •   

用简单的英语:

  • 周围的建议包装
    • 其他围绕之前 更低 优先建议以及
    • 更高 优先级之后。
  • 之前的 建议在委托或继续之前先执行。
  • 之后的建议在执行前先委托。

现在这是一个扩展示例,说明我刚刚写的内容:

驱动程序应用程序:

package de.scrum_master.app;

public class Application {
    public void doSomething(int i) {
        System.out.println("Doing something with " + i);
    }

    public static void main(String[] arg) {
        Application application = new Application();
        application.doSomething(99);
    }
}

看点,变体1:

package de.scrum_master.aspect;

import de.scrum_master.app.Application;

public aspect IntraAspectPrecedence {

    pointcut methodCall(int i) :
        call(void Application.doSomething(int)) && args(i);

    void around(int i): methodCall(i) {
        System.out.println("around1 (pre-proceed) -> " + i);
        proceed(11);
        System.out.println("around1 (post-proceed) -> " + i);
    }

    void around(int i): methodCall(i) {
        System.out.println("around2 (pre-proceed) -> " + i);
        proceed(22);
        System.out.println("around2 (post-proceed) -> " + i);
    }

    before(int i): methodCall(i) {
        System.out.println("before1 -> " + i);
    }

    before(int i): methodCall(i) {
        System.out.println("before2 -> " + i);
    }

    after(int i): methodCall(i) {
        System.out.println("after1 -> " + i);
    }

    after(int i): methodCall(i) {
        System.out.println("after2 -> " + i);
    }

}

伪代码,变体1:

around1(99) {
    around2(11) {
        before1(22) {
            before2(22) {
                proceed(22) {}
            }
        }
    }
}
after2(99) {
    after1(99) {}
}

控制台输出,变体1:

请注意,“after1”在“after2”之前打印,即使它具有较低的偏好,因为之后先委托,然后执行,如上所述。

around1 (pre-proceed) -> 99
around2 (pre-proceed) -> 11
before1 -> 22
before2 -> 22
Doing something with 22
around2 (post-proceed) -> 11
around1 (post-proceed) -> 99
after1 -> 99
after2 -> 99

看点,变体2:

基本上它和以前一样,只有之后的第一个建议具有最高的优先权。

package de.scrum_master.aspect;

import de.scrum_master.app.Application;

public aspect IntraAspectPrecedence {

    pointcut methodCall(int i) :
        call(void Application.doSomething(int)) && args(i);

    after(int i): methodCall(i) {
        System.out.println("after1 -> " + i);
    }

    void around(int i): methodCall(i) {
        System.out.println("around1 (pre-proceed) -> " + i);
        proceed(11);
        System.out.println("around1 (post-proceed) -> " + i);
    }

    void around(int i): methodCall(i) {
        System.out.println("around2 (pre-proceed) -> " + i);
        proceed(22);
        System.out.println("around2 (post-proceed) -> " + i);
    }

    before(int i): methodCall(i) {
        System.out.println("before1 -> " + i);
    }

    before(int i): methodCall(i) {
        System.out.println("before2 -> " + i);
    }

    after(int i): methodCall(i) {
        System.out.println("after2 -> " + i);
    }

}

伪代码,变体2:

这一次,因为 after1 的优先级高于围绕建议的优先级,所以它会在继续之后立即执行,因此在之后发生< em>在周围包装中进行

around1(99) {
    around2(11) {
        before1(22) {
            before2(22) {
                proceed(22) {}
            }
        }
        after1(22) {}
    }
}
after2(99) {}

控制台输出,变体2:

around1 (pre-proceed) -> 99
around2 (pre-proceed) -> 11
before1 -> 22
before2 -> 22
Doing something with 22
after1 -> 22
around2 (post-proceed) -> 11
around1 (post-proceed) -> 99
after2 -> 99