我正在检测第三方应用程序 - 并定义了以下切入点
@Aspect
public class MyAspect {
@Pointcut("execution(some.app.Application.new(..))")
public void appCreation() {
}
@After("appCreation()")
public void afterCreation() {
MyUtil.doSomething();
}
}
现在问题来自MyUtil.doSomething()
最终调用some.app.Application
的构造函数的事实 - 当然这是"检测到"按照我的方面,再次调用MyUtil.doSomething()
,它会调用....你明白了。
我试图在Pointcut定义中放置一个&& !within(MyAspect)
,但它没有帮助。在MyUtil
进一步调用堆栈的情况下,有没有办法抑制切入点的检测?
如果相关:MyUtil.doSomething()
没有直接调用应用程序构造函数,而是在几次中间调用之后
答案 0 :(得分:1)
如果您找不到更优雅的方式,可以随时使用ThreadLocal:
@Aspect
public class MyAspect {
private static final ThreadLocal<Boolean> callInProgress = new ThreadLocal<Boolean>() {
@Override protected Boolean initialValue() {
return false;
}
};
@Pointcut("execution(some.app.Application.new(..))")
public void appCreation() {
}
@After("appCreation()")
public void afterCreation() {
if (!callInProgress.get()) {
callInProgress.set(true);
MyUtil.doSomething();
callInProgress.set(false);
}
}
}
答案 1 :(得分:1)
好的,首先让我们重现您的问题:
Java类:
package de.scrum_master.app;
public class Application {
public Application() {
System.out.println("Creating application");
}
public static void main(String[] args) {
new Application();
}
}
package de.scrum_master.app;
public class MyUtil {
public static void doSomething() {
System.out.println("Doing something");
new Application();
}
}
导致递归的问题因素:
package de.scrum_master.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import de.scrum_master.app.MyUtil;
@Aspect
public class MyAspect {
@Pointcut("execution(*..Application.new(..))")
public void appCreation() {}
@After("appCreation()")
public void afterCreation() {
MyUtil.doSomething();
}
}
控制台日志:
Creating application
Doing something
Creating application
Doing something
(...)
Exception in thread "main" java.lang.StackOverflowError
at sun.nio.cs.UTF_8$Encoder.encodeLoop(Unknown Source)
at java.nio.charset.CharsetEncoder.encode(Unknown Source)
at sun.nio.cs.StreamEncoder.implWrite(Unknown Source)
at sun.nio.cs.StreamEncoder.write(Unknown Source)
at java.io.OutputStreamWriter.write(Unknown Source)
at java.io.BufferedWriter.flushBuffer(Unknown Source)
at java.io.PrintStream.write(Unknown Source)
at java.io.PrintStream.print(Unknown Source)
at java.io.PrintStream.println(Unknown Source)
at de.scrum_master.app.MyUtil.doSomething(MyUtil.java:5)
at de.scrum_master.aspect.MyAspect.afterCreation(MyAspect.aj:16)
at de.scrum_master.app.Application.<init>(Application.java:6)
at de.scrum_master.app.MyUtil.doSomething(MyUtil.java:6)
(...)
现在我们如何避免这个问题?如果建议已经被执行,我们需要避免执行建议,即它是否在当前控制流或cflow()
中。对于建议执行,甚至还有一个特殊的切入点adviceexecution()
。
改进了避免无限递归的方面:
package de.scrum_master.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import de.scrum_master.app.MyUtil;
@Aspect
public class MyAspect {
@Pointcut("adviceexecution() && within(MyAspect)")
public void myAdvice() {}
@Pointcut("execution(*..Application.new(..))")
public void appCreation() {}
@After("appCreation() && !cflow(myAdvice())")
public void afterCreation() {
MyUtil.doSomething();
}
}
更正后的控制台日志:
Creating application
Doing something
Creating application
最后一句话:到目前为止,我并未质疑您的应用程序逻辑。现在我:如果已经创建了一个实用程序方法,那么从实用程序方法创建另一个应用程序是否真的有意义?我想即使这对我来说是一个有趣的AOP练习,真正的问题在于Java代码,而不是在AspectJ代码中。