由于无限循环,AspectJ stackoverflower

时间:2015-08-13 12:26:39

标签: java stack-overflow aop aspectj

我正在检测第三方应用程序 - 并定义了以下切入点

@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()没有直接调用应用程序构造函数,而是在几次中间调用之后

2 个答案:

答案 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代码中。