Spring 对异步调用提供了良好的支持,只需在启动类上添加注解 @EnableAsync,然后在要执行异步操作的类或方法上添加注解 @Async 即可。
开启异步调用
1 2 3 4 5 6 7 8
| @EnableAsync @SpringBootApplication public class WebApplication {
public static void main(String[] args) { SpringApplication.run(WebApplication.class, args); } }
|
创建异步任务
@Async 注解既可以添加在类上,也可以添加在方法上,如果是添加在类上,则该类下的所有方法都将被异步执行。
如果异步方法有返回值,则需要指定其返回对象类型为 Future。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| @Slf4j @Component public class AsyncTask {
@Async public void execute() { log.info("Execution thread name: {}", Thread.currentThread().getName());
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { log.error("Error!", e); } }
@Async public Future<String> executeAndReturn() { log.info("Execution thread name: {}", Thread.currentThread().getName());
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { log.error("Error!", e); }
return new AsyncResult<>("Hello world !"); } }
|
需要注意的是,由于对于 Spring 默认使用代理模式处理 @Async,因此同一类中的本地调用不会被拦截,即 @Async 将会被忽略。
调用异步任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @Slf4j @RequestMapping(("asyncCall")) @RestController public class AsyncCallController { private final AsyncTask asyncTask;
public AsyncCallController(AsyncTask asyncTask) { this.asyncTask = asyncTask; }
@GetMapping("execute") public void execute() { log.info("Main thread name: {}", Thread.currentThread().getName());
asyncTask.execute(); }
@GetMapping("executeAndReturn") public String executeAndReturn() throws ExecutionException, InterruptedException { log.info("Main thread name: {}", Thread.currentThread().getName());
Future<String> future = asyncTask.executeAndReturn(); while (true) { if (future.isDone()) { return future.get(); } else { log.info("Task is working"); TimeUnit.SECONDS.sleep(1); } } }
}
|
配置线程池
Spring 会调用由它管理的 Executor 来处理 @Async。如果没有配置 Executor,它会自己创建 SimpleAsyncTaskExecutor。我们可以通过配置参数来调整它的默认配置。
1 2 3 4 5 6 7 8 9
| spring: task: execution: pool: core-size: 5 max-size: 10 queue-capacity: 200 keep-alive: 10s thread-name-prefix: task-
|
我们也可以自定义 Executor,只需要实现类 AsyncConfigurer 并在其方法 getAsyncExecutor() 中返回自定义的 Executor。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Configuration public class AsyncConfig implements AsyncConfigurer { public static final String ASYNC_EXECUTOR_NAME = "taskExecutor";
private final TaskExecutionProperties properties;
public AsyncConfig(TaskExecutionProperties properties) { this.properties = properties; }
@Bean(ASYNC_EXECUTOR_NAME) @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(properties.getPool().getCoreSize()); executor.setMaxPoolSize(properties.getPool().getMaxSize()); executor.setQueueCapacity(properties.getPool().getQueueCapacity()); executor.setKeepAliveSeconds((int) properties.getPool().getKeepAlive().getSeconds()); executor.setThreadNamePrefix(properties.getThreadNamePrefix()); executor.setTaskDecorator(new ContextCopyingDecorator()); executor.initialize(); return executor; } }
|
如果需要同时配置多个线程池,可以配置多个 Executor Bean。(如果使用了 Spring MVC 中的异步请求,需求将 Executor 的类型设置为 AsyncTaskExecutor)