Я пытаюсь реализовать универсальный класс, который выполняет асинхронно, но я не уверен в семантике.Spring @Async with Future and 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.
Я не уверен насчет исключения в броске предложение подписи метода.
Junit тест:
@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);
}
Мой вопрос, что делать исключения, объявленные в бросках положения о MyCallerImpl.runAsync()?
Единственная причина, по которой я объявил, что это связано с тем, как я вызываю вызываемый. Изначально у меня был следующий в методе асинхронной:
FutureTask<T> futureTask = new FutureTask<T>(callable);
futureTask.run();
return futureTask;
Но когда исключение из отозваны в этом случае, он получает завернут в два раза в ExecutionException, в первый раз, когда FutureTask.run() вызываются в конце концов, FutureTask.Sync.innerRun() ловит исключение и вызывает innnerSetException(), а второй раз, когда AsyncExecutionIntercepter получает результат из Future через Future.get(), который в конечном итоге снова проверяет, существует ли исключение, и выдает новую оболочку ExecutionException ExecutionException, обнаруженное во внутреннем Run()
Я также попытался сделать следующее в методе:
FutureTask<T> futureTask = new FutureTask<T>(callable);
return futureTask;
Я понял, что поскольку AsyncExecutionInterceptor вызывает Future.get(), вызываемый вызов будет вызван немедленно, но это было не так. Он просто зависает на FutureTask.acquireSharedInterruptibly() и никогда не возвращается.
Возможно, я нахожусь здесь над моей головой. Он работает так, как я его настраивал с вызываемым сейчас, но я скорее не хочу, чтобы подпись метода объявляла throw Exception.
Любые советы? Должен ли я забыть об этом общем способе делать асинхронные вызовы с вызываемым?