AspectJ 保持异步方法调用的上下文
AspectJ keep context around async method calls
我是 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");
}
然而,我现在唯一得到的是 WhosebugException(是的,我知道 - 这就是我在这里问的原因)。
编辑:
我偶然发现了 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
中。这样,只要与计时关联的 objects/threads 还活着,您就可以保持计时。
我们需要另一个注释来标记将新对象(本例中的 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
class:
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
作为当前计时器的存储方式,以便能够将其与新对象相关联。您可以为此选择另一种存储方式,但为了示例起见,我尽量保持简单。此外,再次为了简单起见,我省略了对方面中的空值的任何安全检查。您需要自己处理极端情况。
我是 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");
}
然而,我现在唯一得到的是 WhosebugException(是的,我知道 - 这就是我在这里问的原因)。
编辑:
我偶然发现了 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
中。这样,只要与计时关联的 objects/threads 还活着,您就可以保持计时。
我们需要另一个注释来标记将新对象(本例中的 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
class:
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
作为当前计时器的存储方式,以便能够将其与新对象相关联。您可以为此选择另一种存储方式,但为了示例起见,我尽量保持简单。此外,再次为了简单起见,我省略了对方面中的空值的任何安全检查。您需要自己处理极端情况。