Spring @Async with Future和Callable

时间:2013-03-06 16:50:31

标签: spring spring-aop spring-integration spring-annotations

我正在尝试实现一个异步执行可调用的泛型类,但我不确定语义。

@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批注异步执行任何可调用的任意操作。

我不确定方法签名的throws子句中的异常。

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中被包装两次,这是第一次调用FutureTask.run()时FutureTask.Sync.innerRun()捕获异常并调用innnerSetException(并且第二次AsyncExecutionIntercepter通过Future.get()从Future获取结果,最终再次检查是否存在异常并抛出一个新的ExecutionException包装在innerRun()中捕获的ExecutionException

我还尝试在方法中执行以下操作:

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

我认为,由于AsyncExecutionInterceptor调用Future.get(),因此可以立即调用callable,但事实并非如此。它只挂在FutureTask.acquireSharedInterruptibly()上,永远不会返回。

也许我在这里过头了。它的工作原理我现在可以使用callable进行设置,但我宁愿没有方法签名声明抛出异常。

有什么建议吗?我是否应该忘记使用可调用函数执行异步调用的这种通用方法?

1 个答案:

答案 0 :(得分:0)

这里有两层例外。

一个: 导致调用Callable的异常

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

2: 调用callable.call()方法时引起的异常(你的&#34;抛出新的MyException(&#34; foobar&#34;);&#34;)

因为在&#34; callable.call();&#34;之前没有任何其他代码,所以删除已检查的异常是安全的。

更改

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

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