03.Spring Boot与任务

1、整章思维导图

https://gitmind.cn/app/doc/6801008999

在这里插入图片描述

2、异步任务

  • 概述

    ​ 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在Spring 3.x之后,就已经内置了@Async来完美解决这个问题

  • 核心注解

注解说明
@EnableAsync开启基于注解的异步任务支持
@Async标注为异步方法
  • 无返回值异步任务调用

(1)创建无返回值的异步方法

@Async
public void sendSMS() throws Exception{
    System.out.println("调用短信验证码业务方法。。。");
    Long startTime = System.currentTimeMillis();
    Thread.sleep(5000);
    Long endTime = System.currentTimeMillis();
    System.out.println("短信业务执行完成耗时:"+(endTime-startTime));
}

(2)在项目启动类上添加@EnableAsync开启基于注解的异步任务支持

(3)编写调用方法(在测试类中,线程不能正常运行,缺少资源文件,只能在controller里面测试了)

// 注入MyAsyncService类
@Autowired
MyAsyncService myAsyncService;

@GetMapping("/myAsync")
public String TestAsync() throws Exception {
    Long startTime = System.currentTimeMillis();
    myAsyncService.sendSMS();
    Long endTime = System.currentTimeMillis();
    System.out.println("主流程耗时:"+(endTime-startTime));
    System.out.println("运行结束");
    return "success";
}

(4)运行结果

​ 当异步方法没有返回值时,这样主流程在执行异步方法时不会阻塞,而是继续向下执行主流程程序,直接向页面响应结果,而调用的异步方法会作为一个子线程单独执行,直到异步方法执行完成

在这里插入图片描述

  • 有返回值异步任务调用

(1)创建有返回值的异步方法

@Async
public Future<Integer> processA() throws Exception{
    System.out.println("开始分析并统计业务A数据。。。");
    Long startTime = System.currentTimeMillis();
    Thread.sleep(4000);
    int count = 123456;
    Long endTime = System.currentTimeMillis();
    System.out.println("业务A执行耗时:"+(endTime-startTime));
    return new AsyncResult<Integer>(count);
}
@Async
public Future<Integer> processB() throws Exception{
    System.out.println("开始分析并统计业务B数据。。。");
    Long startTime = System.currentTimeMillis();
    Thread.sleep(5000);
    int count = 654321;
    Long endTime = System.currentTimeMillis();
    System.out.println("业务B执行耗时:"+(endTime-startTime));
    return new AsyncResult<Integer>(count);
}

(2)在项目启动类上添加@EnableAsync开启基于注解的异步任务支持

(3)编写调用方法(在测试类中,线程不能正常运行,缺少资源文件,只能在controller里面测试了)

@GetMapping("/Async")
public String TestAsync2() throws Exception{
    Long startTime = System.currentTimeMillis();
    Future<Integer> futureA = myAsyncService.processA();
    Future<Integer> futureB = myAsyncService.processB();
    int total = futureA.get() + futureB.get();
    Long endTime = System.currentTimeMillis();
    System.out.println("异步结果相加:"+total);
    System.out.println("主流程耗时:"+(endTime-startTime));
    System.out.println("运行结束");
    return "success";
}

(4)运行结果

​ 当异步方法有返回值时,主流程在执行异步方法是会有短暂阻塞,需要等待并获取异步方法的返回结果,这样主流程会在最后一个异步方法返回结果后跳出阻塞状态

在这里插入图片描述

3、定时任务

  • 概述

    ​ 项目开发中经常需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前一天的日志信息。Spring为我们提供了异步执行任务调度的方式,提供TaskExecutor 、TaskScheduler 接口,而SpringBoot为我们提供了更方便的注解

  • 核心注解

注解说明
@EnableScheduling开启基于注解方式的定时任务支持
@Scheduled配置定时任务的执行规则
  • @Scheduled属性
属性说明
cron类似与cron的表达式,可以定制定时任务触发的秒、分钟、小时、月中的日、月、周中的日
zone指定cron表达式将被解析的时区,默认情况下,该属性是空字符串(即使用服务器的本地时区
fixedDelay表示上一次任务结束后指定时间后继续执行下一次任务(属性值为long类型)
fixedDelayString表示上一次任务执行结束后指定时间后继续执行下一次任务(属性值为long类型的字符串的形式)
fixedRate表示每隔指定时间执行一次任务(属性值为long类型)
fixedRateString表示每隔指定时间执行一次任务(属性值为long类型的字符串形式)
initialDelay表示在fixedRate或fixedDelay任务第一次执行之前要延迟的毫秒数(属性值为long类型)
initialDelayString表示在fixedRate或fixedDelay任务第一次执行之前要延迟的毫秒数(属性值为long类型的字符串形式)
  • @Scheduled属性说明

(1)cron属性

​ cron属性是@Scheduled定时任务注解中最常用也是最复杂的一个属性,其属性值由类似于cron表达式的6位数组成(但某些cron表达式的字符,在这个注解中不能使用),可以详细地指定定时任务执行的秒、分、小时、日、月、星期

// 表示周一到周五每一分钟执行一次定时任务,第一位表示秒,第二位表示分,第三位表示小时,第四位表示月份中的日,第五位表示月份中的月,第六位表示星期,*代表任意时刻,MON-FRI代码周一到周五,每个字段值都用空格隔开
@Scheduled(cron="0 * * * * MON-FRI")
字段允许值允许的特殊字符
0-59, - * /
0-59, - * /
小时0-23, - * /
日期1-31, - * ? / L W C
月份1-12 、月份对应英文前3个字母(大小写均可), - * /
星期0-7或SUN-SAT 0,7是SUN, - * ? / L C #
特殊字符代表含义示例
,枚举@Scheduled(cron=“1,3,5 * * * * *”)表示任意时间的1、3、5秒都会执行
-区间@Scheduled(cron=“0 0-5 14 * * *”)表示每天下午2:00~2:05期间,每1分钟触发一次
*任意@Scheduled(cron=“0 0 12 * * *”)表示每天中午12:00触发一次
/步长@Scheduled(cron=“0/5 * * * * *”)表示从任意时间的整秒开始,每隔5秒都会执行
?日/星期冲突匹配@Scheduled(cron=“0 * * 26 * ?”)表示每月的26日每一分钟都执行
L最后@Scheduled(cron=“0 0 * L * ?”)表示每月最后一日每一小时都执行

(2)zone属性

​ zone属性主要与cron属性配合使用,指定解析cron属性值的时区。通常情况下,不需要指定zone属性。cron属性值会自动以服务器所在区域作为本地时区进行表达式解析。例如,中国地区服务器的时区通常默认为Asia/Shanghai

(3)fixedDelay/fixedDelayString属性

​ fixedDelay和fixedDelayString属性的作用相似,用于在上一次任务执行完毕后,一旦到达指定时间就继续执行下一次任务。两个的区别在属性值的类型不同,fixedDelay属性值为long类型,而fixedDelayString属性值是数值字符串

// 在任务结束后开始计时
// 表示在程序启动后会立即执行一次定时任务,然后在任务结束后,相隔5秒重复执行定时任务
@Scheduled(fixedDelay=5000)
@Scheduled(fixedDelayString="5000")

(4)fixedRate/fixedRateString属性

​ fixedRate和fixedRateString属性的作用类似,指定每相隔一段时间重复执行一次定时任务,它们的主要区别是属性值的类型不同,其中fixedRate属性值为long类型,fixedRateString属性值为数值字符串

// 在任务开始是就开始计时,若相隔时间小于任务执行时间,则下一任务在前任务结束后立即执行
// 表示在程序启动后会立即执行一次定时任务,然后在任务结束后,相隔5秒重复执行定时任务
@Scheduled(fixedRate=5000)
@Scheduled(fixedRateRate="5000")

(5)initialDelay/initialDelayString属性

​ initialDelay和initialDelayString属性的作用类似,主要是与fixedDelay/fixedDelayString

或者fixedRate/fixedRateString属性配合使用,指定定时任务第一次执行的延迟时间,然后在按照各自相隔时间重复执行任务

// 在程序启动后,会延迟1秒后再执行第一次定时任务,然后以后每个任务相隔5秒重复执行
@Scheduled(initialDelay=1000,fixedDelay=5000)
@Scheduled(initialDelay=1000,fixedRate=5000)
  • 定时任务示例

(1)编写定时任务业务处理方法

@Service
public class ScheduledTaskService {
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private Integer count1 = 1;
    private Integer count2 = 1;
    private Integer count3 = 1;
    @Scheduled(fixedRate = 6000)
    public void scheduledTaskImmediately(){
        System.out.println(String.format("fixedRate第%s此执行,当前时间为:%s",count1++,dateFormat.format(new Date())));
    }
    @Scheduled(fixedDelay = 6000)
    public void scheduledTaskAfterSleep() throws Exception{
        System.out.println(String.format("fixedDelay第%s此执行,当前时间为:%s",count2++,dateFormat.format(new Date())));
        Thread.sleep(10000);
    }
    @Scheduled(cron = "0 * * * * *")
    public void scheduledTaskCron(){
        System.out.println(String.format("cron第%s次执行,当前时间为:%s",count3++,dateFormat.format(new Date())));
    }
}

(2)在项目启动类上添加@EnableScheduling开启基于注解的定时任务支持,不需要调用方法,任务会自动执行

4、邮件服务

  • 添加邮件依赖启动器
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
  • 添加邮件服务配置
spring:
  mail:
    host: smtp.qq.com
    port: 587
    username: 1239674647@qq.com
    password: wgplfktfgdupigfc
    default-encoding: utf-8
    # 邮件服务超时时间配置
    properties:
      mail:
        smtp:
          connectiontimeout: 5000
          timeout: 3000
          writetimeout: 5000
  • 发送纯文本邮件
@Autowired
private JavaMailSenderImpl mailSender;
@Value("${spring.mail.username}")
private String from;
public void sendSimpleEmail(String to,String subject,String text){
    SimpleMailMessage message = new SimpleMailMessage();
    // 发送者
    message.setFrom(from);
    // 接收者
    message.setTo(to);
    // 主题
    message.setSubject(subject);
    // 正文
    message.setText(text);
    try {
        mailSender.send(message);
        System.out.println("纯文本邮件发送成功");
    }catch (MailException e){
        System.out.println("纯文本邮件发送失败");
        e.printStackTrace();
    }
}
  • 发送带附件和图片的邮件
/**
 * 发送复杂邮件
 * @param to            收件人地址
 * @param subject       邮件标题
 * @param text          邮件内容
 * @param filePath      附件地址
 * @param recId         静态资源唯一标识
 * @param recPath       静态资源地址
 */
public void sendComplexEmail(String to,String subject,String text,String filePath,String recId,String recPath){
    MimeMessage message = mailSender.createMimeMessage();
    try {
        // 将邮件消息封装进MimeMessageHelper类,并设置multipart多部件使用true
        MimeMessageHelper helper = new MimeMessageHelper(message,true);
        helper.setFrom(from);
        helper.setTo(to);
        helper.setSubject(subject);
        // 解析html为true
        helper.setText(text,true);
        // 设置邮件静态资源
        FileSystemResource resource = new FileSystemResource(new File(recPath));
        helper.addInline(recId,resource);
        // 设置邮件附件
        FileSystemResource file = new FileSystemResource(new File(filePath));
        String fileName = filePath.substring((filePath.lastIndexOf(File.separator)));
        helper.addAttachment(fileName,file);
        // 发送邮件
        mailSender.send(message);
        System.out.println("复杂邮件发送成功");
    } catch (MessagingException e) {
        System.out.println("复杂邮件发送失败");
        e.printStackTrace();
    }
}
  • 发送模板邮件

(1)添加Thymeleaf模板引擎依赖启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

(2)编写html页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
   <meta charset="UTF-8">
   <title>发送模板邮件测试</title>
</head>
<body>
<div><span th:text="${msg}" style="color: aqua"></span></div>
</body>
</html>

(3)编写发送模板邮件服务

public void sendTemplateEmail(String to,String subject,String content){
    MimeMessage message = mailSender.createMimeMessage();
    try {
        MimeMessageHelper helper = new MimeMessageHelper(message,true);
        helper.setFrom(from);
        helper.setTo(to);
        helper.setText(content,true);
        mailSender.send(message);
        System.out.println("模板邮件发送成功");
    }catch (MessagingException e){
        System.out.println("模板邮件发送失败");
        e.printStackTrace();
    }
}

(4)发送邮件

//thymeleaf解析引擎
@Autowired
TemplateEngine templateEngine;
@Test
public void sendTemplateEmailTest(){
    String to = "1239674647@qq.com";
    String subject = "【模板邮件】标题";
    // 对变量赋值
    Context context = new Context();
    context.setVariable("msg","模板邮件测试");
    // 使用TemplateEngine设置处理的模板页面
    String emailContent = templateEngine.process("email",context);
    sendEmailService.sendTemplateEmail(to,subject,emailContent);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值