请考虑这个简单的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'。
答案 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