在Spring Framework中分别使用TaskExecutor和TaskScheduler接口提供异步执行和任务调度的抽象,本节我们着重讲解基于TaskExecutor支撑的的注解@Async如何实现异步处理的。
在Spring中可以在方法上添加@Async注释,以便异步调用该方法。换句话说,调用者将在调用含有@Async注释的方法时立即返回,并且该方法的实际执行将发生在Spring TaskExecutor异步处理器线程中。需要注意的是该注解@Async默认是不会解析的,SpringBoot中需要加上@EnableAsync来启动。
下面我们看如何使用@Async注解进行异步处理,如下代码:
@Async
public void dosomthingAsync() {
System.out.println("--dosomthingAsync begin---");
// 模拟异步处理
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("--dosomthingAsync end---");
}
如上代码在方法dosomthingAsync上添加了@Async的注解,所以当我们调用dosomthingAsync方法时候,该方法会马上返回。
另外使用@Async可以有返回值,因为它们将在运行时由调用者以“正常”方式调用,而不是由容器管理的调度任务TaskExecutor自动调用。例如,以下是使用@Async注解的合法方法:
@Component
public class AsyncTask {
...
@Async
public CompletableFuture<String> dosomthingAsyncFuture() {
System.out.println("--dosomthingAsync begin---");
CompletableFuture<String> future = new CompletableFuture<String>();
// 模拟异步处理
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
future.complete("ok");
System.out.println("--dosomthingAsync end---");
return future;
}
}
如上代码调用该方法后,该方法会马上返回一个CompletableFuture对象,如果你一直持有这个CompletableFuture对象,那么等dosomthingAsyncFuture内业务处理异步处理完毕后,就可以从dosomthingAsyncFuture的get()方法获取到执行结果。
那么Spring框架是如何做到我们dosomthingAsyncFuture时候会马上返回一个CompletableFuture那?其实其对该类进行了代理,经过代理后的上面的方法类似于:
public class AsynTaskProxy {
public AsyncTask getAsyncTask() {
return asyncTask;
}
public void setAsyncTask(AsyncTask asyncTask) {
this.asyncTask = asyncTask;
}
private AsyncTask asyncTask;
private TaskExecutor executor = new SimpleAsyncTaskExecutor();
public CompletableFuture<String> dosomthingAsyncFuture() {
return CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
try {
return asyncTask.dosomthingAsyncFuture().get();
} catch (Throwable e) {
throw new CompletionException(e);
}
}
},executor);
}
}
Spring会对AsyncTask类使用类似的AsynTaskProxy进行代理,并且会把AsynTask的实例注入到AsynTaskProxy内部,当我们调用AsynTask的dosomthingAsyncFuture方法时候,实际调用的是AsynTaskProxy的dosomthingAsyncFuture方法,后者则使用 CompletableFuture.supplyAsync开启了一个异步任务(其马上返回一个 CompletableFuture对象),并且使用默认的SimpleAsyncTaskExecutor线程池做为异步处理线程,然后异步任务内在具体调用了 AsyncTask实例的dosomthingAsyncFuture方法,并且在返回的future上获取执行结果。