在新线程中调用连接点时,Spring Aspect失败

时间:2011-08-22 11:49:37

标签: java spring-aop

我使用Spring 3.0.5和Around方面。

@Around方面非常有效。 AOP表达式针对一堆bean的接口。

方面在invokation之前和之后执行一些逻辑:

 @Around(...)
    public Object monitor(ProceedingJoinPoint pjp) throws Throwable {
         // some code
         Obj o =  pjp.proceed();
         // some code
    }

没什么大不了的。

现在,我试图创建另一个方面,如果截获的方法花费太长时间会引发异常。

private static ExecutorService executor = Executors.newCachedThreadPool();

@Around(...)
public Object monitor(ProceedingJoinPoint pjp) throws Throwable {

Object obj = null;

Callable<Object> task = new Callable<Object>() {
    public Object call() {
        return pjp.proceed();
    }
};
Future<Object> future = executor.submit(task);
try {
    obj = future.get(timeout, TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {
    ...
} catch (InterruptedException e) {
    // we ignore this one...
} catch (ExecutionException e) {
    throw e.getCause(); // rethrow any exception raised by the invoked method
} finally {
    future.cancel(true); // may or may not desire this
}

return obj;
}

当我执行仅应用此方面的代码时,我得到以下异常:

  

java.lang.RuntimeException:java.lang.IllegalStateException:No   找到MethodInvocation:检查AOP调用是否正在进行中,   并且ExposeInvocationInterceptor位于拦截器链中。

Spring documentation我读到:

&#34; Class ExposeInvocationInterceptor

将当前MethodInvocation公开为线程局部对象的拦截器。&#34;

所以看起来目标丢失了,因为我基本上开始一个新线程并且新线程无法访问本地线程。有没有办法解决这个问题或更好的方法?

由于

2 个答案:

答案 0 :(得分:2)

解决方案非常简单。检查方法花费时间的方面必须是方面“链”中的最后一个方面。我在Aspect上使用了@Order注释,使其成为最后一个要执行的注释。

这就是诀窍。

如果Aspect不是最后一个要执行的,则新线程无法访问包含ExposeInvocationInterceptor类的ThreadLocal变量。

答案 1 :(得分:0)

如果pjp.proceed()调用适合中断,您可以尝试从另一个线程中断当前线程。例如。你的方面看起来像:

    new Interrupter(Thread.currentThread()).start();
    // Following call will get interrupted if it takes too long
    try {
        return pjp.proceed();
    } catch (InterruptedException ex) {
        // do something?
    }

中断类的类似于:

static class Interrupter extends Thread {

    private final Thread other;

    Interrupter(final Thread other) {
        this.other = other;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(500); // or whatever your timeout is
        } catch (final InterruptedException e) {
            e.printStackTrace();
        }
        if (other.isAlive()) {
            other.interrupt();
        }
    }
}