Spring Boot | Spring Boot 整合 “异步任务“ 的实现

目录:

    • 一、异步任务
      • [1.1 "无返回值" 异步任务调用 :](#1.1 "无返回值" 异步任务调用 :)
        • [① 创建项目](#① 创建项目)
        • [② 编写 "异步调用方法" ( 使用 @Async 注解 )](#② 编写 "异步调用方法" ( 使用 @Async 注解 ))
        • [③ "主程序启动类"中 开启基于 "注解" 的异步任务支持 ( 使用@EnableAsync注解 )](#③ "主程序启动类"中 开启基于 "注解" 的异步任务支持 ( 使用@EnableAsync注解 ))
        • [④ 编写 "控制层" 相关方法](#④ 编写 "控制层" 相关方法)
        • [⑤ "异步任务" 效果测试](#⑤ "异步任务" 效果测试)
      • [1.2 "有返回值" 异步任务调用 :](#1.2 "有返回值" 异步任务调用 :)
        • [① 创建项目](#① 创建项目)
        • [② 编写 "异步调用方法" ( 使用 @Async 注解 )](#② 编写 "异步调用方法" ( 使用 @Async 注解 ))
        • [③ "主程序启动类"中 开启基于 "注解" 的异步任务支持 ( 使用@EnableAsync注解 )](#③ "主程序启动类"中 开启基于 "注解" 的异步任务支持 ( 使用@EnableAsync注解 ))
        • [④ 编写 "控制层" 相关方法](#④ 编写 "控制层" 相关方法)
        • [⑤ "异步任务" 效果测试](#⑤ "异步任务" 效果测试)

作者简介 :一只大皮卡丘,计算机专业学生,正在努力学习、努力敲代码中! 让我们一起继续努力学习!

该文章参考学习教材 为:
《Spring Boot企业级开发教程》 黑马程序员 / 编著

文章以课本知识点 + 代码为主线,结合自己看书学习过程中的理解和感悟 ,最终成就了该文章

文章用于本人学习使用 , 同时希望能帮助大家。

欢迎大家点赞👍 收藏⭐ 关注💖哦!!!

(侵权可联系我,进行删除,如果雷同,纯属巧合)


  • 开发 Web 应用时,** 多数应用** 都具备 "任务调度" 功能 。( Spring Boot 任务管理 ) 常见的任务 包括 ① 异步任务
    ② 定时任务③ 发"邮件任务"
  • 我们将以数据库报表 为例看看任务调度如何 帮助改善 "系统设计"报表 可能是错综复杂 的,用户可能需要很长时间找到需要的报表数据,此时,我们可以在这个报表应用添加"异步任务" 来 减少用户等待时间 ,从而 提高用户体验
  • 除此之外 ,还可以在 报表应用添加"定时任务" 和 "** 邮件任务** " ,以便用户可以安排在任何他们 需要的时间 "定时"生成报表 ,并在 Email 中发送 。下面将介绍如何使用Spring Boot开发 这些常见的任务

一、异步任务

  • Web 应用开发 中,大多数情况 都是通过 同步方式 完成数据交互处理 ,但是,当处理与第三方系统的交互时,容易 造成 响应迟缓的情况,之前大部分都是** 使用 "多线程" 完成此类任务** ,除此之外,还可以使用 异步调用 的方式完美解决这个问题
  • 根据 异步处理方式的不同 ,可以将 异步任务的调用分为** ①** 无返回值异步任务调用 有返回值异步任务 调用。

1.1 "无返回值" 异步任务调用 :

  • 实际开发 中,项目 可能会 向新注册用户 "发送短信验证码" ,这时,可以考虑使用 异步任务调用的方式实现一方面是因为用户对这个** 时效性要求 不是特别高** ( 实际发送验证码客户手机 上是要1-几秒的时间 的 ,但提示说"验证码"已发送 这个可是几乎马上响应 的,这两个动作有时间差前者满后者快 ,就是因为使用了 "异步任务 " , 让"慢的动作 " 和 "快的动作 " 区分开来 ,这个效果是用"异步任务实现的" ,) 。

    一方面特定时间范围内没有收到验证码用户 可以点击再次发送验证码 。下面将使用Spring Boot框架演示 这种场景需求 ,进一步说明 无返回值的异步任务调用

    ps
    实际情况 时,点了获得验证码 之后,系统快速回应说验证码已发送 ( 假设其是"主程序 " ),然后 验证码过了一段时间才实际发到手机上 ( 假设其是 "次程序 " )的,这两个动作是有 时间差 的,怎么实现这个效果 ? 或者说 不用这种效果会有什么缺点 呢?---在次程序这里使用了 "异步任务"

    如果不使用 "异步任务 " , 那么此时的i效果 就是 : 点了"发送验证码"按钮后要等几秒 之后才说"验证码已发送 ",然后 "实际的" 验证码也发送到手机 上,此时两个动作是没什么时间差 的,这种效果好吗?

    ---- 那肯定是不好 的,因为验证码过一段时间 才发送到手机上的, 那为什么要让"主程序"也陪着一起等几秒呢 ? ,所以用了"异步任务 "之后,主程序 就可以快速做出响应 ,然后"次程序"执行多久就多久,客户只需等待就好。

① 创建项目
  • 创建项目 :

    需要说明的是Spring 框架 提供 了对 异步任务支持SpringBoot框架继承 了这一 异步任务 功能。在 Spring Boot 中整合异步任务 时,只需在项目中引入Web 模块 中的 Web 依赖就可以使用这种异步任务功能。

② 编写 "异步调用方法" ( 使用 @Async 注解 )
  • 在项目中创建 service包 ,并在该包下创建一个业务实现类 : MyAsyncService ,在该类中编写模拟用户短信验证码发送方法例子代码如下 :

    MyAsyncService.java

    java 复制代码
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Service;
    
    @Service //加入IOC容器
    public class MyAsyncService { //"异步任务" 业务类
    
        @Async //标记该方法为"异步方法" (此时该异步方法还不会生效,要在主程序启动类中添加 @EnableAsync注解 )
        //该方法用于模拟"发送短信验证码"
        public void sendSms() throws Exception{
            System.out.println("--------------------");
            System.out.println("调用短信验证码业务方法...,验证码发送中,请稍等...");
            /*
                System.currentTimeMillis():
               返回当前时间与1970年1月1日00:00:00 GMT之间的 "毫秒数"。这个方法通常用于生成"时间戳",它可以帮助我们跟踪和记录事件发生的时间。
             */
            Long startTime = System.currentTimeMillis();
            Thread.sleep(5000); //线程"睡眠"5秒
            Long endTime = System.currentTimeMillis();
            System.out.println("验证码真正实际发送到手机中~");
            System.out.println("短信业务执行完成耗时:(次流程) "+(endTime-startTime));
        }
    
    }
③ "主程序启动类"中 开启基于 "注解" 的异步任务支持 ( 使用@EnableAsync注解 )
  • 上一步编写的 用户短信验证码发送业务方法 中,使用 @Async 注解标记了** 异步方法** ,如果想要异步方法生效 ,还需要使用 @EnableAsync 注解 开启基于注解的** 异步任务支持** ( 让被 "@Async注解 " 标记的"异步方法"生效 )。@EnableAsync 注解 通常会添加在 项目启动类 上,例子代码如下

    Chapter25Application.java ( 主程序启动类 ):

    java 复制代码
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.scheduling.annotation.EnableAsync;
    
    @EnableAsync //开启基于"注解"的异步任务支持 ( 让被 "@Async注解" 标记的"异步方法"生效 )
    @SpringBootApplication
    public class Chapter25Application {
    
        public static void main(String[] args) {
           SpringApplication.run(Chapter25Application.class, args);
        }
    }
④ 编写 "控制层" 相关方法
  • 在项目中创建 controller ,在该包下创建类 MyAsyncControler用于 调用异步方法。在该类中模拟编写用户短信验证码发送处理方法,例子代码如下 :

    MyAsyncService.java :

    java 复制代码
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @ResponseBody //将方法的返回值转换为"指定类型"且存入到响应体中
    @Controller //将该类加入到IOC容器中
    public class MyAsyncController {// "控制层"相关内容
    
        @Autowired
        private MyAsyncService myAsyncService;
    
        @GetMapping("/sendSMS")
        public String sendSms() throws Exception {
            long startTime = System.currentTimeMillis();
            //调用"业务层"中的"发送短信方法"
            myAsyncService.sendSms();
            long endTime = System.currentTimeMillis();
            System.out.println("验证码已发送...( 客户得到提示:验证码已发送~ )");
            System.out.println("主流程耗时: "+(endTime-startTime));
            return "success"; //响应该字符串信息给前端
        }
    }
⑤ "异步任务" 效果测试
  • 启动项目后 ,在浏览器上访问 http://localhost:8080/sendSMS ,测试"异步任务"请求,此时**控制台打印信息** 如下图所示 :


    演示结果可以看出 ,执行 sendSMS( )方法 并调用异步方法 处理短信业务时 ,在 很短的时间内(1毫秒** )完成了主流程的执行** ,并向页面响应主流程结果 ,而在主流程打印输出方法之前 调用的 异步方法 经过一段时间后才执行完毕 ( 主次流程 之间存在"时间差 " ,此时为了不影响用户体验 ,可用 "异步任务 " ) 。因此,从执行结果可以发现 ,案例中无返回值异步任务调用成功

    需要说明 的是,上述案例中的异步方法没有返回值 的,这样 主流程执行异步方法不会阻塞,而是继续向下执行主流程程序 ,直接向页面响应结果 ,而调用异步方法 会作为 一个子线程单独执行 ,直到 异步方法执行完成

  • 注释 @Async注解重启动项目访问路径 ,看看"有"异步任务 和 "无"异步任务区别 :


1.2 "有返回值" 异步任务调用 :

  • 实际开发 中,项目中可能会涉及 有返回值异步任务调用 。例如,一个程序 中需要 调用两个业务方法 对相关 业务数据统计分析 ,并将统计结果汇总 。下面j将使用 Spring Boot框架演示这种场景需求 ,进一步说明 有返回值异步任务 调用。
① 创建项目
  • 创建项目 :

    需要说明的是Spring 框架 提供 了对 异步任务支持SpringBoot框架继承 了这一 异步任务 功能。在 Spring Boot 中整合异步任务 时,只需在项目中引入Web 模块 中的 Web 依赖就可以使用这种异步任务功能。

② 编写 "异步调用方法" ( 使用 @Async 注解 )
  • 在项目中创建 service包 ,并在该包下创建一个业务实现类 : MyAsyncService ,在该类中编写 两个模拟有返回值异步任务业务处理方法
    例子代码如下 :

    MyAsyncService.java

    java 复制代码
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.scheduling.annotation.AsyncResult;
    import org.springframework.stereotype.Service;
    
    import java.util.concurrent.Future;
    
    @Service //加入IOC容器
    public class MyAsyncService { //"异步任务" 业务类
    
        @Async
        public Future<Integer> processA() throws Exception{ //该"异步方法"有一定的"处理时间",并且返回统计结果。
            System.out.println("开始分析统计业务A数据.....");
            Long startTime = System.currentTimeMillis();
            //休眠
            Thread.sleep(4000); //线程"睡眠"5秒
            //模拟定义一个假的统计结果
            int count = 123456;
            Long endTime = System.currentTimeMillis();
            System.out.println("业务A数据统计耗时: "+(endTime-startTime));
            //使用AsyncResult对象封装 返回的"异步结果数据",该方法的返回值为 Future类型
            return new AsyncResult<Integer>(count);
        }
    
    
    
        @Async
        public Future<Integer> processB() throws Exception{ //该"异步方法"有一定的"处理时间",并且返回统计结果。
            System.out.println("开始分析统计业务B数据.....");
            Long startTime = System.currentTimeMillis();
            //休眠
            Thread.sleep(5000); //线程"睡眠"5秒
            //模拟定义一个假的统计结果
            int count = 654321;
            Long endTime = System.currentTimeMillis();
            System.out.println("业务B数据统计耗时: "+(endTime-startTime));
            //使用AsyncResult对象封装 返回的"异步结果数据",该方法的返回值为 Future类型
            return new AsyncResult<Integer>(count);
        }
    
    }
    复制代码
③ "主程序启动类"中 开启基于 "注解" 的异步任务支持 ( 使用@EnableAsync注解 )
  • 上一步编写的 用户短信验证码发送业务方法 中,使用 @Async 注解标记了** 异步方法** ,如果想要异步方法生效 ,还需要使用 @EnableAsync 注解 开启基于注解的** 异步任务支持** ( 让被 "@Async注解 " 标记的"异步方法"生效 )。@EnableAsync 注解 通常会添加在 项目启动类 上,例子代码如下

    Chapter25Application.java ( 主程序启动类 ):

    java 复制代码
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.scheduling.annotation.EnableAsync;
    
    @EnableAsync //开启基于"注解"的异步任务支持 ( 让被 "@Async注解" 标记的"异步方法"生效 )
    @SpringBootApplication
    public class Chapter25Application {
    
        public static void main(String[] args) {
           SpringApplication.run(Chapter25Application.class, args);
        }
    }
④ 编写 "控制层" 相关方法
  • 编写 "控制层" 相关方法

    MyAsyncController.java

    java 复制代码
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.concurrent.Future;
    
    @ResponseBody //将方法的返回值转换为"指定类型"且存入到响应体中 --让return 字符串时,不被默认识别为一个"视图页面"
    @Controller //将该类加入到IOC容器中
    public class MyAsyncController {// "控制层"相关内容
    
        @Autowired
        private MyAsyncService myAsyncService;
    
        @GetMapping("/statistics")
        public String statistics() throws Exception {
            long startTime = System.currentTimeMillis();
            //"有返回值"的 "异步任务"
            Future<Integer> futureA = myAsyncService.processA();
            Future<Integer> futureB = myAsyncService.processB();
            int total = futureA.get()+futureB.get();
            System.out.println("异步任务数据统计汇总结果: "+total);
            long endtTime = System.currentTimeMillis();
    
            System.out.println("主流程耗时: "+(endtTime-startTime));
            return "success"; //响应该字符串信息给前端
        }
    
    }
⑤ "异步任务" 效果测试
  • 启动项目后 ,在浏览器上访问 http://localhost:8080/statistics ,测试"异步任务"请求,此时 控制台打印信息下图所示 :

    演示结果可以看出 ,执行 statistics( )方法调用异步方法处理业务数据统计 时,需要 耗费一定的时间 (5004毫秒)完成主流程的执行 ,并向页面响应结果 ,而在主流程打印输出结果之前一直等待着业务A 和业务 B 两个异步方法异步调用处理结果汇总 。因此,从执行结果可以发现 ,案例中 有返回值异步任务调用成功


需要说明的是,上述的异步方法是** 有返回值的, 当返回值较多时** 主流程执行异步方法 是会有 短暂阻塞需要等待并获取异步方法的 返回结果,而** 调用的两个异步方法会作为 两个子线程 "并行执行"** ,直到异步方法执行完成返回结果 ,这样 主流程 会在 最后一个异步方法返回结果后 "跳出阻塞状态"

相关推荐
柏油30 分钟前
可视化 MySQL binlog 监听方案
数据库·后端·mysql
舒一笑44 分钟前
Started TttttApplication in 0.257 seconds (没有 Web 依赖导致 JVM 正常退出)
jvm·spring boot·后端
M1A11 小时前
Java Enum 类:优雅的常量定义与管理方式(深度解析)
后端
AAA修煤气灶刘哥2 小时前
别再懵了!Spring、Spring Boot、Spring MVC 的区别,一篇讲透
后端·面试
柏油2 小时前
MySQL 字符集 utf8 与 utf8mb4
数据库·后端·mysql
程序猿阿越2 小时前
Kafka源码(三)发送消息-客户端
java·后端·源码阅读
javadaydayup2 小时前
Apollo 凭什么能 “干掉” 本地配置?
spring boot·后端·spring
似水流年流不尽思念2 小时前
Spring MVC 中的 DTO 对象的字段被 transient 修饰,可以被序列化吗?
后端·面试
武子康2 小时前
大数据-70 Kafka 日志清理:删除、压缩及混合模式最佳实践
大数据·后端·kafka