Spring Aspect терпит неудачу, когда точка соединения вызывается в новом потоке

Я использую Spring 3.0.5 с аспектом Around.

Аспект @Around работает отлично. Выражение АОП предназначено для интерфейсов группы компонентов.

Аспект выполняет некоторую логику до и после вызова:

 @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: MethodInvocation не найдено: убедитесь, что выполняется вызов AOP и что ExposeInvocationInterceptor находится в цепочке перехватчиков.

Из документации Spring Я прочитал:

"Класс ExposeInvocationInterceptor

Перехватчик, который предоставляет текущий вызов метода MethodInvocation как локальный объект потока».

Итак, похоже, что цель потеряна, потому что я в основном запускаю новый поток, а новый поток не имеет доступа к локальному потоку. Есть ли способ решить эту проблему или лучший подход?

Спасибо


person Luciano Fiandesio    schedule 22.08.2011    source источник


Ответы (2)


Решение было весьма тривиальным. Аспект, который проверяет, сколько времени занимает метод, должен быть последним в «цепочке» аспектов. Я использовал аннотацию @Order для Aspect, чтобы сделать его последним для выполнения.

Это помогло.

Если аспект выполняется не последним, новый поток не может получить доступ к переменной ThreadLocal, содержащей класс ExposeInvocationInterceptor.

person Luciano Fiandesio    schedule 23.08.2011

Вы можете попробовать прервать текущий поток из другого потока, если вызов 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?
    }

где класс Interrupter будет выглядеть примерно так:

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();
        }
    }
}
person artbristol    schedule 22.08.2011