Spring @Async с Future и Callable

Я пытаюсь реализовать универсальный класс, который асинхронно выполняет вызываемый объект, но я не уверен в семантике.

@Component
public class MyCallerImpl implements MyCaller {

    @Async
    @Override
    public <T> Future<T> runAsync(Callable<T> callable) throws Exception {

        return new AsyncResult<T>(callable.call());
    }
}

По сути, этот компонент выполняет произвольные действия из любого вызываемого объекта асинхронно, используя аннотацию @Async.

Я не уверен в Exception в предложении throws сигнатуры метода.

Юнит-тест:

@ContextConfiguration("classpath:test-config.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class RunnerTest{

    @Resource(name="myCallerImpl")
    private MyCaller myCaller;

    @Test
    public void testException(){

        final Callable<String> callable = new Callable<String>(){
            @Override
            public String call() throws Exception{
                throw new MyException("foobar");
            }
        };

        try
        {
            final Future<String> future = myCaller.runAsync(callable); // this can throw Exception due to Callable.call()
            future.get(); // this can throw InterruptedException and ExecutionException 
        }
        catch (final InterruptedException ie)
        {
            // do someting
        }
        catch (final ExecutionException ee)
        {
            // we want to check the cause
            final Throwable cause = ee.getCause();
            assertTrue(cause instanceof MyException);
        }
        catch (final Exception e)
        {
            // Not sure what to do here. 
            // Must be caught as it is declared to 
            // be thrown from the MyCaller.runAsync() method
            // but nothing will really ever get here 
            // since the method is @Async and any exception will be
            // wrapped by an ExecutionException and thrown during Future.get()

            fail("this is unexpected);
        }

Мой вопрос: что делать с Exception, объявленным в предложении throws MyCallerImpl.runAsync()?

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

FutureTask<T> futureTask = new FutureTask<T>(callable);
futureTask.run();
return futureTask;

Но когда исключение генерируется из вызываемого объекта в этом экземпляре, оно дважды оборачивается в ExecutionException, первый раз, когда вызывается FutureTask.run(), в конечном итоге FutureTask.Sync.innerRun() перехватывает исключение и вызывает innnerSetException() и второй раз, когда AsyncExecutionIntercepter получает результат из Future через Future.get(), который в конечном итоге снова проверяет, есть ли исключение, и выдает новое ExecutionException, обертывающее ExecutionException, пойманное в innerRun()

Я также попытался сделать следующее в методе:

FutureTask<T> futureTask = new FutureTask<T>(callable);
return futureTask;

Я полагал, что, поскольку AsyncExecutionInterceptor вызывает Future.get(), вызываемый объект будет вызываться немедленно, но это было не так. Он просто зависает на FutureTask.acquireSharedInterruptably() и никогда не возвращается.

Может быть, я здесь не в своей тарелке. Он работает так, как я настроил его с вызываемым сейчас, но я предпочитаю, чтобы сигнатура метода не объявляла исключение throws.

Любой совет? Должен ли я забыть об этом общем способе выполнения асинхронных вызовов с вызываемым объектом?


person CAL5101    schedule 06.03.2013    source источник


Ответы (1)


Здесь есть 2 уровня исключения.

один: исключение, ведущее к вызову Callable

if (string.equals("X")){ callable.call();}

два: исключение, вызванное при вызове метода callable.call() (ваш "throw new MyException("foobar");")

поскольку у вас нет другого кода до "callable.call();", было бы безопасно удалить проверенное исключение.

Изменять

public <T> Future<T> runAsync(Callable<T> callable) throws Exception

to

public <T> Future<T> runAsync(Callable<T> callable) 

кроме того, вы можете закодировать его таким образом

            final Future<String> future = myCaller.runAsync(callable);
  try
        {
            future.get(); // this can throw InterruptedException and ExecutionException 
        }
        catch (final InterruptedException ie)
        {
            // do someting
        }
        catch (final ExecutionException ee)
        {
            // we want to check the cause
            final Throwable cause = ee.getCause();
            assertTrue(cause instanceof MyException);
        }
person Titi Wangsa Bin Damhore    schedule 24.11.2013