Springboot任务管理(邮件发送异步同步,定时)


前言

这章节来学习Springboot的异步处理


提示:以下是本篇文章正文内容,下面案例可供参考

一、任务管理的介绍

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

二、使用步骤

需要用到的依赖启动器

<!--        添加Thymeleaf模板引擎依赖启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!--        添加邮件服务的依赖启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

1.异步任务

无返回值异步任务调用

1.Spring Boot 项目创建引入Web依赖
2.编写异步调用方法

代码如下(示例):

创建业务异步处理类

@Service
public class MyAsyncService {
    @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));
    }}
3.开启基于注解的异步任务支持
在启动类中加入核心注解@EnableAsync
4.编写控制层业务调用方法
@RestController
public class MyAsyncController {
    @Autowired
    private MyAsyncService myService;
    @GetMapping("/sendSMS")
    public String sendSMS() throws Exception {
        Long startTime = System.currentTimeMillis();
        myService.sendSMS();
        Long endTime = System.currentTimeMillis();
        System.out.println("主流程耗时: "+(endTime-startTime));
        return "success";}

}
5.异步任务效果测试

在这里插入图片描述

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

有返回值异步任务调用

编写异步调用方法,模拟有返回值的业务处理

在MyAsyncService业务处理类中,添加两个模拟有返回值的异步任务业务处理方法

@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);
}

编写控制层业务调用方法,模拟业务数据分析统计

在MyAsyncController业务处理类中,编写业务数据分析统计的请求处理方法

@GetMapping("/statistics")
public String statistics() throws Exception {
	Long startTime = System.currentTimeMillis();
	Future<Integer> futureA = myService.processA();
	Future<Integer> futureB = myService.processB();
	int total = futureA.get() + futureB.get();
	System.out.println("异步任务数据统计汇总结果: "+total);
	Long endTime = System.currentTimeMillis();
	System.out.println("主流程耗时: "+(endTime-startTime));
	return "success";}

在这里插入图片描述

statistics()方法执行时候,需要耗费时间完成主流程的执行,并向页面响应效果,所以出结果之前一直等待业务A和业务B两个异步方法的异步调用处理和结果汇总,因为是有返回值的所以当方法运行时会出现阻塞状态等待异步方法的返回结果,而调用的两个异步方法会作为两个子线程并行执行,知道异步方法执行完成并返回结果,这样主流程会在最后一个异步方法返回结果后跳出阻塞状态

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

定时任务

相关注解

@EnableScheduling
@Scheduled

在这里插入图片描述

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

新建一个定时任务管理的业务处理类ScheduledTaskService,并在该类中编写对应的定时任务处理方法。使用@Scheduled注解声明了三个定时任务方法,这三个方法定制的执行规则基本相同,都是每隔1分钟重复执行一次定时任务,在使用fixedDelay属性的方法scheduledTaskAfterSleep()中,使用Thread.sleep(10000)模拟该定时任务处理耗时为10秒钟。

@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 = 60000)
    public void scheduledTaskImmediately() {
        System.out.println(String.format("fixedRate第%s次执行,当前时间为:%s",
                count1++, dateFormat.format(new Date())));
    }

    @Scheduled(fixedDelay = 60000)
    public void scheduledTaskAfterSleep() throws InterruptedException {
        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
3.定时任务效果测试

在这里插入图片描述

配置@Scheduled注解的fixedRate和fixedDelay属性的定时方法会立即执行一次,配置cron属性的定时方法会在整数分钟时间点首次执行;接着,配置fixedRate和cron属性的方法会每隔1分钟重复执行一次定时任务,而配置fixedDelay属性的方法是在上一次方法执行完成后再相隔1分钟重复执行一次定时任务。

发送纯文本邮件

1.添加邮件服务的依赖启动器
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-mail</artifactId>
</dependency>

当添加上述依赖后,Spring Boot自动配置的邮件服务会生效,在邮件发送任务时,可以直接使用Spring框架提供的JavaMailSender接口或者它的实现类JavaMailSenderImpl邮件发送。

2.添加邮件服务配置
# 发件人邮服务器相关配置
spring.mail.host=smtp.qq.com//发送邮件的相关地址
spring.mail.port=587
# 配置个人QQ账户和密码(密码是加密后的授权码)
spring.mail.username=个人邮箱
spring.mail.password=个人邮箱授权码
spring.mail.default-encoding=UTF-8
# 邮件服务超时时间配置
spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=3000
spring.mail.properties.mail.smtp.writetimeout=5000

3.定制邮件发送服务

新建一个邮件发送任务管理的业务处理类SendEmailService,编写了一个发送纯文本邮件的sendSimpleEmail()方法,在该方法中通过SimpleMailMessage类定制了邮件信息的发件人地址(From)、收件人地址(To)、邮件标题(Subject)和邮件内容(Text),最后使用JavaMailSenderImpl的send()方法实现纯文本邮件发送。

@Service
public class SendEmailService {
    @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.getMessage());e.printStackTrace();} }}

4.纯文本邮件发送效果测试
  @Autowired
    private SendEmailService sendEmailService;
    @Test
    public void sendSimpleMailTest() {
        String to = "收件人的邮箱";//输入收入人邮箱
        String subject = "我是标题";
        String text = "Spring Boot纯文本邮件发送内容测试.....";//输入邮件文本
        sendEmailService.sendSimpleEmail(to, subject, text);
    }

发送带附件和图片的文件

1.定制邮件发送服务

打开之前创建的邮件发送任务的业务处理类SendEmailService,在该类中编写一个发送带附件和图片邮件的业务方法sendComplexEmail() ,该方法需要接收的参数除了基本的发送信息外,还包括静态资源唯一标识、静态资源路径和附件路径。

public void sendComplexEmail(String to,String subject,String text,String filePath,String rscId,
                                  String rscPath){
        MimeMessage message = mailSender.createMimeMessage();
        try {
        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setFrom(from);
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(text, true);
        FileSystemResource res = new FileSystemResource(new File(rscPath));
        helper.addInline(rscId, res);
        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.getMessage());e.printStackTrace();}}

2.邮件发送效果测试

  @Test
    public void sendComplexEmailTest() {
        String to="收件人邮箱@qq.com";
        String subject="【复杂邮件】标题";
        StringBuilder text = new StringBuilder();
        text.append("<html><head></head>");
        text.append("<body><h1>测试文本!</h1>");
        String rscId = "img001";
        text.append("<img src='cid:" +rscId+"'/></body>");text.append("</html>");
        String rscPath="F:\\email\\newyear.jpg";
        String filePath="F:\\email\\发送的.txt";//对应自己电脑的文档位置
        sendEmailService.sendComplexEmail(to,subject,text.toString(),
                filePath,rscId,rscPath);}

发送模板邮件

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

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

2.定制模板邮件

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>用户验证码</title>
</head>
<body>
    <div><span th:text="${username}">XXX</span>&nbsp;先生/女士,您好:</div>
    <P style="text-indent: 2em">您的新用户验证码为<span th:text="${code}" 
                           style="color: cornflowerblue">123456</span>,请妥善保管。</P>
</body></html>

3.定制邮件发送服务

在业务处理类SendEmailService中编写一个发送Html模板邮件的业务方法,sendTemplateEmail()方法主要用于处理Html内容(包括Thymeleaf邮件模板)的邮件发送,在定制Html模板邮件信息时,、使用了MimeMessageHelper类对邮件信息进行封装处理。

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.setSubject(subject);helper.setText(content, true);
            mailSender.send(message);System.out.println("模板邮件发送成功");
        } catch (MessagingException e)
        {System.out.println("模板邮件发送失败 "+e.getMessage());
            e.printStackTrace();}}

4.模板邮件发送效果测试

在项目测试类中添加一个方法调用前面编写的Html模板邮件发送方法测试邮件发送效果,先使用@Autowired注解引入了Thymeleaf提供的模板引擎解析器TemplateEngine,然后定制了模板邮件发送所需的参数。

@Autowired
    private TemplateEngine templateEngine;
    @Test
    public void sendTemplateEmailTest() {
        String to="收件人邮箱@qq.com";
        String subject="文件名";
        Context context = new Context();
        context.setVariable("username", "收件人");//用户名
        context.setVariable("code", "1314520");//验证码
        String emailContent = templateEngine.process("emailTemplate_vercode", context);
        sendEmailService.sendTemplateEmail(to,subject,emailContent);}

例如:以上就是今天要讲的内容,本文仅仅简单介绍了消息异步以及定时的使用。