我是AspectJ的新手,我试图弄清楚如何保持/跟踪多个异步方法调用的上下文。想象以下代码:

@TimerStart
public void doSomething() throws InterruptedException {
    Thread.sleep(1000);
    MyCallable callable = new MyCallable();
    Future future = executorService.submit(callable );
}

private class MyCallable implements Callable {
    @Override
    public Object call() throws Exception {
        someOtherMethod();
        return null;
    }

    @TimerEnd
    private void someOtherMethod() throws InterruptedException {
        Thread.sleep(1000);
    }
}


我想测量@TimerStart和@TimerEnd之间经过的时间。我现在正面临两个问题:


我如何在各个方面之间保持对象。一个方面中的字段似乎都是静态的,那么并发问题呢?
如何获得两条建议,一条在@TimerStart之前执行,另一条在@TimerEnd之后执行。


目前,我有一些类似的思路:

public aspect TimerAspect {

    pointcut timerStart(Object object, TimerStart timed):
        execution(@TimerStart * *(..)) && this(object) && @annotation(timed);

    pointcut timerStop(Object object, TimerEnd timed):
        cflow(execution(@TimerEnd * *(..)) && this(object) && @annotation(timed) && !within(FlowTimerAspect));


    before(Object object, TimerStart timed): timerStart(object, timed)  {
        System.out.println("##### Flow timer START");
    }

    after(Object object, TimerEnd timed): timerStop(object, timed)  {
        System.out.println("##### Flow timer STOP");
    }


但是,我现在唯一得到的是StackOverflowException(是的,我知道-这就是为什么我在这里询问)。

编辑:
我偶然发现percflow似乎只有在@TimerStart和@TimerEnd出现在同一线程中时才有效。建议高度赞赏!

public aspect TimerAspect percflow(timerStart(Object, TimerStart)) {

    private long context;

    pointcut timerStart(Object object, TimerStart timed):
            execution(@TimerStart * *(..)) && this(object) && @annotation(timed);

    pointcut timerStop(Object object, TimerEnd timed):
            execution(@TimerEnd * *(..)) && this(object) && @annotation(timed);


    before(Object object, TimerStart timed): timerStart(object, timed)  {
        context = System.currentTimeMillis();
    }

    after(Object object, TimerEnd timed): timerStop(object, timed)  {
        long passed = System.currentTimeMillis() - context;
        System.out.println("passed time: " + passed);
    }
}

最佳答案

由于您打算在测量时切换线程,因此percflow实例化方法将无济于事。您必须坚持默认的单例纵横比,并将感兴趣对象的计时值保留在WeakHashMap中。这样,只要与该时间关联的对象/线程处于活动状态,就可以保留时间。
我们将需要另一个注释来标记将新对象(在此示例中为Callable)与您的时间相关联的事件。我们称之为@TimerJoin@TimerJoin注释将类似于您现有的@TimerStart@TimerEnd注释。您的测量方面将如下所示。

import java.util.Map;
import java.util.WeakHashMap;

public aspect TimerAspect {

    private final Map<Object, Timer> objectTiming = new WeakHashMap<>();
    private final ThreadLocal<Timer> currentThreadTimer = new ThreadLocal<>();

    pointcut timerStart(Object object):
            execution(@TimerStart * *(..)) && this(object);

    pointcut timerStop(Object object):
            execution(@TimerEnd * *(..)) && this(object);

    pointcut timerJoin(Object object):
        (execution(@TimerJoin * *(..)) || execution(@TimerJoin *.new(..)) )
        && this(object);

    before(Object object): timerStart(object) {
        Timer timer = new Timer();
        timer.start();
        objectTiming.put(object, timer);
        currentThreadTimer.set(timer);
        System.out.println("##### Flow timer START");
    }

    before(Object object): timerJoin(object) {
        Timer timing = currentThreadTimer.get();
        objectTiming.put(object, timing);
        System.out.println("##### Flow timer JOIN");
    }

    after(Object object): timerStop(object) {
        Timer timing = objectTiming.get(object);
        timing.stop();
        System.out.println("##### Flow timer STOP");
        System.out.println("Elapsed: " + timing.getElapsed());
    }

}


和简单的Timer.java类:

public class Timer {

    private long start;
    private long stop;

    public long getStart() {
        return start;
    }

    public long getStop() {
        return stop;
    }

    public void start() {
        start = System.currentTimeMillis();
    }

    public void stop() {
        stop = System.currentTimeMillis();
    }

    public long getElapsed() {
        return stop-start;
    }
}


修改可调用对象以将其标记为加入当前线程上的计时器:

private class MyCallable implements Callable {

    @TimerJoin
    public MyCallable() {
    }

    @Override
    public Object call() throws Exception {
        someOtherMethod();
        return null;
    }

    @TimerEnd
    private void someOtherMethod() throws InterruptedException {
        Thread.sleep(1000);
    }
}


您的其余代码将相同。

您可能会注意到,方面正在使用ThreadLocal作为当前计时器的存储方式,以便能够将其与新对象相关联。您可以为此选择另一种存储,但是出于示例的原因,我尝试使其保持简单。同样,再次为简单起见,我在方面没有对空值进行任何安全检查。您需要自己处理极端情况。

09-28 05:06