Spring Aspect не работает, когда точка соединения вызывается в новом потоке - PullRequest
5 голосов
/ 22 августа 2011

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

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

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

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

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

"Класс ExposeInvocationInterceptor

Перехватчик, который отображает текущее MethodInvocation как локальный объект потока. "

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

Спасибо

Ответы [ 2 ]

2 голосов
/ 23 августа 2011

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

Это добилось цели.

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

0 голосов
/ 22 августа 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?
    }

где класс прерывателя будет что-то вроде:

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