异步调用,对应的是同步调用。
同步调用:指程序按照 定义顺序 依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行;异步调用:指程序在顺序执行时,不等待异步调用的语句返回结果,就执行后面的程序。在 Spring Framework 的 Spring Task 模块,提供了 @Async 注解,可以添加在方法上,自动实现该方法的异步调用。因此不需要特别引入依赖,只要引入springboot的依赖即可。
创建 Application.java类,配置 @SpringBootApplication 注解。
@SpringBootApplication @EnableAsync // 开启 @Async 的支持 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }在类上添加 @EnableAsync 注解,启用异步功能。
创建 AsyncConfig类,配置异常处理器
@Configuration @EnableAsync // 开启 @Async 的支持 public class AsyncConfig implements AsyncConfigurer { @Autowired private GlobalAsyncExceptionHandler exceptionHandler; @Override public Executor getAsyncExecutor() { return null; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return exceptionHandler; } }配置类里使用了@EnableAsync,Application类中就可以省略@EnableAsync注释
创建 GlobalAsyncExceptionHandler 类,全局统一的异步调用异常的处理器。
/** * AsyncUncaughtExceptionHandler 只能拦截返回类型非 Future 的异步调用方法 */ @Component public class GlobalAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { private final Logger logger = LoggerFactory.getLogger(getClass()); @Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { logger.error("[handleUncaughtException][method({}) params({}) 发生异常]", method, params, ex); } }AsyncUncaughtExceptionHandler 只能拦截返回类型非 Future 的异步调用方法。
在方法前加@Async注释,即启用了异步方法。
@Slf4j @Service public class AsyncService extends AbstractService{ @Async public Integer execute01Async() { return this.execute01(); } @Async public Integer execute02Async() { return this.execute02(); } @Async public Future<Integer> execute01AsyncWithFuture() { return AsyncResult.forValue(this.execute01()); } @Async public Future<Integer> execute02AsyncWithFuture() { return AsyncResult.forValue(this.execute02()); } @Async public ListenableFuture<Integer> execute01AsyncWithListenableFuture() { try { return AsyncResult.forValue(this.execute02()); } catch (Throwable ex) { return AsyncResult.forExecutionException(ex); } } public Integer execute01() { log.info("[execute01]"); sleep(10); return 1; } public Integer execute02() { log.info("[execute02]"); sleep(5); return 2; } private static void sleep(int seconds) { try { Thread.sleep(seconds * 1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } @Async public Integer zhaoDaoNvPengYou(Integer a, Integer b) { throw new RuntimeException("程序员不需要女朋友"); } }往往会出现主线程执行完成,异步方法还未执行。
在一些业务场景中,我们希望达到异步调用的效果,同时主线程阻塞等待异步调用的结果。示例中增加 #execute01() 和 #execute02() 的异步调用,并返回 Future 对象
task01的结果:
task02的结果:
task03的结果:
task04的结果:
testZhaoDaoNvPengYou的结果:
