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 アノテーションを使用して任意の callable から非同期に任意のアクションを実行します。
メソッド シグネチャの throws 句の Exception についてよくわかりません。
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() の throws 句で宣言された例外をどうするかということです。
私がそれを宣言した唯一の理由は、callable を呼び出す方法のためです。もともと、非同期メソッドには次のものがありました。
FutureTask<T> futureTask = new FutureTask<T>(callable);
futureTask.run();
return futureTask;
しかし、そのインスタンスで callable から例外がスローされると、ExecutionException で 2 回ラップされます。最初に FutureTask.run() が呼び出されると、最終的に FutureTask.Sync.innerRun() が例外をキャッチし、innnerSetException() を呼び出します。 AsyncExecutionIntercepter が Future.get() を介して Future から結果を取得する 2 回目。これは最終的に例外があるかどうかを再びチェックし、innerRun() でキャッチされた ExecutionException をラップする新しい ExecutionException をスローします。
メソッドで次のことも試みました。
FutureTask<T> futureTask = new FutureTask<T>(callable);
return futureTask;
AsyncExecutionInterceptor が Future.get() を呼び出すので、callable がすぐに呼び出されると思っていましたが、そうではありませんでした。FutureTask.acquireSharedInterruptively() でハングアップするだけで、戻りません。
多分私はここで私の頭の上にいます。これは、呼び出し可能オブジェクトを使用してセットアップした方法で機能しますが、メソッド シグネチャで throws Exception を宣言する必要はありません。
何かアドバイス?callable で非同期呼び出しを行うこの一般的な方法を忘れるべきですか?