有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java AspectJ围绕异步方法调用保留上下文

我是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);
    }
}

共 (1) 个答案

  1. # 1 楼答案

    由于您计划在测量时切换线程,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;
        }
    }
    

    修改callable以将其标记为加入当前线程上的计时器:

    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);
        }
    }
    

    剩下的代码将是相同的

    您可能会注意到,aspect使用ThreadLocal作为当前计时器的存储方式,以便能够将其与新对象关联。您可以为此选择另一种存储方式,但出于示例的考虑,我尽量保持简单。同样,为了简单起见,我在方面中省略了任何空值的安全检查。你需要自己处理角落里的案子