spring3支持@Async注解的异步任务,之前大家都是通过使用如线程池来完成,spring3也是使用这种方式,但更简单。
其具体实现在:org.springframework.aop.interceptor.AsyncExecutionInterceptor,是一个方法拦截器,其invoke方法的部分代码如下:
- Future<?> result = determineAsyncExecutor(specificMethod).submit(
- new Callable<Object>() {
- public Object call() throws Exception {
- try {
- Object result = invocation.proceed();
- if (result instanceof Future) {
- return ((Future<?>) result).get();
- }
- }
- catch (Throwable ex) {
- ReflectionUtils.rethrowException(ex);
- }
- return null;
- }
- });
Future<?> result = determineAsyncExecutor(specificMethod).submit(
new Callable<Object>() {
public Object call() throws Exception {
try {
Object result = invocation.proceed();
if (result instanceof Future) {
return ((Future<?>) result).get();
}
}
catch (Throwable ex) {
ReflectionUtils.rethrowException(ex);
}
return null;
}
});
即把当前任务的调用提交给线程池,很简单。
1、测试无事务的异步任务
这个相对来说比较简单:
1.1、设置任务的返回值为Future:
- public Future sendSystemMessage(Long[] receiverIds, Message message);
public Future sendSystemMessage(Long[] receiverIds, Message message);
1.2、调用future.get();等待任务结束。
- Future future = messageApi.sendSystemMessage(userIds, message);
- future.get();
Future future = messageApi.sendSystemMessage(userIds, message);
future.get();
这个很简单。
2、测试带事务的异步任务
因为是带事务的,所以异步任务肯定要启动一个线程来执行任务,所以无法在主线程回滚,造成数据会commit到数据库,这在集成测试时肯定是不行的;解决方案是移除异步任务:
2.1、使用spring profile,在测试环境下不执行<task:annotation-driven>即可。
2.2、使用我提供的工具类,在测试时移除异步支持即可:
- //移除异步支持
- if(AopProxyUtils.isAsync(messageApi)) {
- AopProxyUtils.removeAsync(messageApi);
- }
//移除异步支持
if(AopProxyUtils.isAsync(messageApi)) {
AopProxyUtils.removeAsync(messageApi);
}
测试类可以参考MessageApiServiceIT.java
工具类下载 AopProxyUtils.java
3、包级别测试
- @Async
- public void sendSystemMessage() {
- sendSystemMessageInner();
- }
- void sendSystemMessageInner() {
- //测试时测试这个方法即可
- }
@Async
public void sendSystemMessage() {
sendSystemMessageInner();
}
void sendSystemMessageInner() {
//测试时测试这个方法即可
}
这样测试时测试这个包级别的sendSystemMessageInner方法即可
其实更好的做法是spring内部提供支持,支持这样异步调用的测试。