2016-01-05 23 views
1

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

回答

2

既然你打算切換線程同時測量,該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作爲當前計時器的存儲方式,以便能夠將其與新對象相關聯。您可以爲此選擇另一種存儲,但爲了示例的目的,我儘量保持簡單。另外,爲了簡單起見,我在這方面忽略了對空值的任何安全檢查。你需要自己處理角落案例。

+0

根據我所描述的,您的代碼正常工作,謝謝。但是,它有點脆弱 - 例如。當在使用@TimerStart註解的方法運行之前構造Callable時,它當然無法將兩個線程鏈接在一起。 – schneida

+0

的確很脆弱,因此需要通過不同的理智檢查來加強,仔細考慮在意外使用模式下會發生什麼情況。您可能希望優雅地處理這些情況,而不會干擾應該運行的原始代碼,因爲這只是應用程序的測量方面。處理所有這些微妙之處已經超出了我的答案範圍,我只想告訴你一種方法來克服你一直面臨的最初問題。 –

+0

好的,謝謝你的回答:-) – schneida