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 两个异步方法异步调用处理结果汇总 。因此,从执行结果可以发现 ,案例中 有返回值异步任务调用成功


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

相关推荐
向前看-3 小时前
验证码机制
前端·后端
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭4 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
超爱吃士力架4 小时前
邀请逻辑
java·linux·后端
AskHarries6 小时前
Spring Cloud OpenFeign快速入门demo
spring boot·后端
isolusion7 小时前
Springboot的创建方式
java·spring boot·后端
Yvemil78 小时前
《开启微服务之旅:Spring Boot Web开发举例》(一)
前端·spring boot·微服务
zjw_rp8 小时前
Spring-AOP
java·后端·spring·spring-aop
TodoCoder8 小时前
【编程思想】CopyOnWrite是如何解决高并发场景中的读写瓶颈?
java·后端·面试
凌虚9 小时前
Kubernetes APF(API 优先级和公平调度)简介
后端·程序员·kubernetes
星河梦瑾9 小时前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全