由于无限循环导致的 AspectJ stackoverflowerror
AspectJ stackoverflowerror due to infinite loop
我正在检测第 3 方应用程序 - 并定义了以下切入点
@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
的构造函数 - 这当然是 "detected" 在我看来 MyUtil.doSomething()
被调用再次调用......你明白了。
我试图在 Pointcut 定义中加入 && !within(MyAspect)
但它没有帮助。在 MyUtil
位于调用堆栈的更上方的情况下,是否有任何方法可以抑制切入点的检测?
如果相关:MyUtil.doSomething()
不是直接调用应用程序构造函数,而是在几次中间调用之后调用
如果您找不到更优雅的方式,您可以随时使用 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);
}
}
}
好的,首先让我们重现您的问题:
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.WhosebugError
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 代码中。
我正在检测第 3 方应用程序 - 并定义了以下切入点
@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
的构造函数 - 这当然是 "detected" 在我看来 MyUtil.doSomething()
被调用再次调用......你明白了。
我试图在 Pointcut 定义中加入 && !within(MyAspect)
但它没有帮助。在 MyUtil
位于调用堆栈的更上方的情况下,是否有任何方法可以抑制切入点的检测?
如果相关:MyUtil.doSomething()
不是直接调用应用程序构造函数,而是在几次中间调用之后调用
如果您找不到更优雅的方式,您可以随时使用 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);
}
}
}
好的,首先让我们重现您的问题:
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.WhosebugError
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 代码中。