前言

SpringBoot想必大家都用过,但是大家平时使用发布的接口大都是同步的,那么你知道如何优雅的实现异步呢?

这篇文章就是关于如何在Spring Boot中实现异步行为的。但首先,让我们看看同步和异步之间的区别。

  • 同步编程:在同步编程中,任务一次执行一个,只有当一个任务完成时,下一个任务才会被解除阻塞。
  • 异步编程:在异步编程中,可以同时执行多个任务。您可以在上一个任务完成之前转到另一个任务。

Spring Boot中,我们可以使用@Async注解来实现异步行为。

欢迎关注个人公众号【JAVA旭阳】交流沟通

实现步骤

  1. 定义一个异步服务接口AsyncService.java
public interface AsyncService {

    void asyncMethod() throws InterruptedException;

    Future<String> futureMethod() throws InterruptedException;
}
  1. 实现定义的接口AsyncServiceImpl.java
@Service
@Slf4j
public class AsyncServiceImpl implements AsyncService { @Async
@Override
public void asyncMethod() throws InterruptedException {
Thread.sleep(3000);
log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName());
} @Async
@Override
public Future<String> futureMethod() throws InterruptedException {
Thread.sleep(5000);
log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName());
return new AsyncResult<>("task Done");
}
}
  • AsyncServiceImpl 是一个 spring 管理的 bean
  • 您的异步方法必须是公共的,而且是被@Async注解修饰。
  • 返回类型被限制为 voidFuture
  1. 定义一个控制器AsyncController.java
@EnableAsync
@RestController
@Slf4j
public class AsyncController {
@Autowired
AsyncService asyncService; @GetMapping("/async")
public String asyncCallerMethod() throws InterruptedException {
long start = System.currentTimeMillis();
log.info("call async method, thread name: [{}]", Thread.currentThread().getName());
asyncService.asyncMethod();
String response = "task completes in :" +
(System.currentTimeMillis() - start) + "milliseconds";
return response;
} @GetMapping("/asyncFuture")
public String asyncFuture() throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
log.info("call async method, thread name: [{}]", Thread.currentThread().getName());
Future<String> future = asyncService.futureMethod();
// 阻塞获取结果
String taskResult = future.get();
String response = taskResult + "task completes in :" +
(System.currentTimeMillis() - start) + "milliseconds";
return response;
}
}
  • 关键点,需要添加启用异步的注解@EnableAsync ,当然这个注解加在其他地方也ok得。
  • 当外部调用该接口时,asyncMethod() 将由默认任务执行程序创建的另一个线程执行,主线程不需要等待完成异步方法执行。
  1. 运行一下

现在我们运行一下看看,是不是异步返回的。

  • 可以看到调用/async接口,最终一步调用了方法。

  • 调用/asyncFuture,发现返回5秒多,难道不是异步的吗?其实也是异步的,看日志可以看出来,只不过我们返回的是Future,调用Futrue.get()是阻塞的。

自定义异步任务执行器和异常处理

我们现在看看如果异常方法中报错了会怎么样?修改异步代码如下所示,会抛运行时异常:

再次执行异步接口,如下所示,会使用默认的线程池和异常处理。

我们也可以自定义异步方法的处理异常和异步任务执行器,我们需要配置 AsyncUncaughtExceptionHandler,如下代码所示:

@Configuration
public class AsynConfiguration extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new
ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(4);
executor.setThreadNamePrefix("asyn-task-thread-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler
getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() { @Override
public void handleUncaughtException(Throwable ex,
Method method, Object... params) {
System.out.println("Exception: " + ex.getMessage());
System.out.println("Method Name: " + method.getName());
ex.printStackTrace();
}
};
}
}

再次运行,得到的结果如下:

@Async如何工作的?

必须通过使用 @EnableAsync注解注解主应用程序类或任何直接或间接异步方法调用程序类来启用异步支持。主要通过代理模式实现,默认模式是 Proxy,另一种是 AspectJ。代理模式只允许通过代理拦截调用。永远不要从定义它的同一个类调用异步方法,它不会起作用。

当使用 @Async对方法进行注解时,它会根据“proxyTargetClass”属性为该对象创建一个代理。当 spring 执行这个方法时,默认情况下它会搜索关联的线程池定义。上下文中唯一的 spring 框架 TaskExecutor bean 或名为“taskExecutor”的 Executor bean。如果这两者都不可解析,默认会使用spring框架SimpleAsyncTaskExecutor来处理异步方法的执行。

总结

在本文中,我们演示了在 spring boot 中如何使用 @Async 注解和异步方法中的异常处理实现异步行为。我们可以在一个接口中,需要访问不同的资源,比如异步调用各个其他服务的接口,可以使用@Async,然后将结果通过Future的方式阻塞汇总,不失为一个提高性能的好方法。

欢迎关注个人公众号【JAVA旭阳】交流沟通

Spring Boot中如何优雅地实现异步调用?的更多相关文章

  1. spring boot 学习(十一)使用@Async实现异步调用

    使用@Async实现异步调用 什么是”异步调用”与”同步调用” “同步调用”就是程序按照一定的顺序依次执行,,每一行程序代码必须等上一行代码执行完毕才能执行:”异步调用”则是只要上一行代码执行,无需等 ...

  2. spring boot中使用@Async实现异步调用任务

    本篇文章主要介绍了spring boot中使用@Async实现异步调用任务,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 什么是“异步调用”? “异步调用”对应的是“同步 ...

  3. Spring Boot中使用@Async实现异步调用

    在Spring Boot中,我们只需要通过使用@Async注解就能简单的将原来的同步函数变为异步函数,为了让@Async注解能够生效,还需要在Spring Boot的主程序中配置@EnableAsyn ...

  4. Spring Boot中实现异步调用之@Async

    一.什么是异步调用 “异步调用”对应的是“同步调用”,同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行:异步调用指程序在顺序执行时,不等待异步调用 的语句返回结果 ...

  5. 56. spring boot中使用@Async实现异步调用【从零开始学Spring Boot】

    什么是"异步调用"? "异步调用"对应的是"同步调用",同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执 ...

  6. Spring Boot中使用@Async实现异步调用,加速任务的执行!

    什么是"异步调用"?"异步调用"对应的是"同步调用",同步调用指程序按照定义顺序依次执行,每一行程序都必须等待上一行程序执行完成之后才能执行 ...

  7. 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

  8. Spring Boot中使用Spring-data-jpa让数据访问更简单、更优雅

    在上一篇Spring中使用JdbcTemplate访问数据库中介绍了一种基本的数据访问方式,结合构建RESTful API和使用Thymeleaf模板引擎渲染Web视图的内容就已经可以完成App服务端 ...

  9. 利用 Spring Boot 中的 @ConfigurationProperties,优雅绑定配置参数

    使用 @Value("${property}") 注释注入配置属性有时会很麻烦,尤其是当你使用多个属性或你的数据是分层的时候. Spring Boot 引入了一个可替换的方案 -- ...

  10. Spring Boot中使用MyBatis注解配置详解(1)

    之前在Spring Boot中整合MyBatis时,采用了注解的配置方式,相信很多人还是比较喜欢这种优雅的方式的,也收到不少读者朋友的反馈和问题,主要集中于针对各种场景下注解如何使用,下面就对几种常见 ...

随机推荐

  1. 手把手教你用Burpsuite+夜神模拟器抓安卓(Android 7)http+https包

    (1)进入正题,bp证书下载(下载证书至电脑):打开bp,配置好代理,浏览器访问http://127.0.0.1:8080 下载证书,记住保存路径,注意别修改证书后缀(der)!!! (2)准备一个L ...

  2. [转]Selenium私房菜系列1 -- Selenium简介

    一.Selenium是什么? Selenium是ThroughtWorks公司一个强大的开源Web功能测试工具系列,本系列现在主要包括以下4款: 1.Selenium Core:支持DHTML的测试案 ...

  3. Leetcode习题集-链表

    这里记录一些我刷题的思路方便之后进行复习重温,同时也方便进行添加 P141-环形链表 class Solution { public: bool hasCycle(ListNode *head) { ...

  4. mumu模拟器pin码忘记,如何找回

    1.进入mumu安装目录:D:\Program Files (x86)\MuMu\emulator\nemu\vmonitor\bin 2.通过cmd进入mumu的bin目录,输入命令:adb_ser ...

  5. keras构建1D-CNN模型

    接触过深度学习的人一定听过keras,为了学习的方便,接下来将要仔细的讲解一下这keras库是如何构建1D-CNN深度学习框架的 from keras.datasets import imdb fro ...

  6. iOS学习十一之步进控制器UIStepper

    步进控制器也就是进行离散式数据调节的常用视图控件. 在viewDidLoad()方法中加入下面的方法,即可完成基本功能. override func viewDidLoad() { super.vie ...

  7. Spring Cloud学习记录

    Eureka和zookeeper都是注册中心为什么zookeeper不适合? 1.CAP原则.一致性,可用性,分区容错性,最多满足两种.zookeeper遵循CP原则,实际项目中不应该为了一致性失去可 ...

  8. 修改AXI UART D16550 FIFO深度的过程记录

    仅限于AXI UART 16550 v. 2.0,其他版本可能存在差异,经过实际测试,可以将fifo深度从默认的16成功修改为32.128和256.参考了两篇帖子中提到的方法,分别是修改AXI UAR ...

  9. linux基础命令4

    用户和组群账户管理 用户的 角色是通过UID(用户ID号)来标识的,每个用户的UID都是不同的. 在Linux系统中有三大类用户,分别是root 用户.系统用户和普通用户. root用户UID为0.r ...

  10. 数字子串的和 str2int

    UVA1673 这道题可以用广义后缀自动机,不过陈锋老师给我们讲了一个巧妙地方法,使得这道题可以用普通的后缀自动机做. 题目大意: 给出NNN个完全由数字组成的字符串.计算将这个NNN的字符串的所有子 ...