简体   繁体   中英

Spring Aspect fails when join point is invoked in new thread

I'm using Spring 3.0.5 with an Around aspect.

The @Around aspect works perfectly. The AOP expression targets the interfaces of a bunch of beans.

The aspect executes some logic before and after the invokation:

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

No big deal.

Now, I'm trying to create another aspect that throws an exception if the intercepted method takes too long.

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

When I execute the code with only this aspect applied I get the following exception:

java.lang.RuntimeException: java.lang.IllegalStateException: No MethodInvocation found: Check that an AOP invocation is in progress, and that the ExposeInvocationInterceptor is in the interceptor chain.

From the Spring documentation I read:

"Class ExposeInvocationInterceptor

Interceptor that exposes the current MethodInvocation as a thread-local object."

So it looks like that the target got lost because I basically start a new thread and the new thread doesn't have access to thread local. Is there a way to solve this problem or a better approach?

Thanks

The solution was quite trivial. The Aspect that checks how long a method takes must be the last in the "chain" of aspects. I have used the @Order annotation on the Aspect to make it the last one to be executed.

That did the trick.

If the Aspect is not the last to be executed, the new Thread is not able to access the ThreadLocal variable containing the ExposeInvocationInterceptor class.

You could try interrupting the current thread from another thread, if the pjp.proceed() call is amenable to interruption. Eg your aspect looks like:

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

where the Interrupter class would be something like:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM