赞
踩
开发web应用时,多数应用都具备任务调度功能,提高用户体验。
名词解释:
任务调度:任务调度指的是计算机系统中的一种机制,用于安排和执行特定任务或作业的时间和顺序。这些任务可以是一次性的,也可以是重复性的,如定期执行的任务。
异步任务是指在程序执行过程中,某个操作或任务的调用不会阻塞当前线程,而是会以非阻塞的方式在后台或另一个线程中执行,允许程序继续执行其他操作或任务。
阻塞(Blocking):当一个任务或操作在执行过程中,如果需要等待某个条件满足或某个事件发生,而此时程序无法继续执行其他任务,称为阻塞。
非阻塞(Non-Blocking):当一个任务或操作在执行过程中,如果无需等待某个条件满足或某个事件发生,而可以立即返回,并让程序继续。
在项目启动类加上@EnableAsync注解开启基于注解的异步任务支持。
@EnableAsync
@SpringBootApplication
public class SpringtaskApplication {
public static void main(String[] args) {
SpringApplication.run(SpringtaskApplication.class, args);
}
}
创建一个MyAsyncService类,在该类中编写对应的sendSMS()异步任务。
代码如下(示例):
@Async
//@Async:把方法变成异步执行。
//Async注解修饰的方法必须是public方法。
public void sendSMS() throws Exception {
System.out.println("调用短信验证码业务方法...");
Long startTime = System.currentTimeMillis();
Thread.sleep(5000);
Long endTime = System.currentTimeMillis();
System.out.println("短信业务执行完成耗时:" + (endTime - startTime));
}
创建一个MyAsyncController类,在该类中编写对应的Controller。
@ResponseBody
@GetMapping("/sendSMS")
public String sendSMS() throws Exception {
Long startTime = System.currentTimeMillis();
myService.sendSMS();
Long endTime = System.currentTimeMillis();
System.out.println("主流程耗时: " + (endTime - startTime));
return "success";
}
调用sendSMS接口输出结果:
可以看出主程序并没有等待myService.sendSMS()执行完,才往下执行。这也就说明了异步以非阻塞的方式在后台或另一个线程中执行。
继续在MyAsyncService类添加processA()方法。
代码如下(示例):
@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));
// 创建一个已完成的CompletableFuture,结果为指定的数值
// CompletableFuture 是 Java 中用于支持异步编程和处理异步结果的类
//
return CompletableFuture.completedFuture(count);
}
继续在MyAsyncController类添加statistics()方法。
@ResponseBody
@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接口输出结果:
当异步任务有返回值,而你使用了这个返回值,那么程序就会出现阻塞,等待异步任务执行完后程序才继续往下执行。而不使用返回值,程序就不会出现阻塞。
@ResponseBody
@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";
}
不使用返回值输出结果:
定时任务指的是在预先定义的时间点或时间间隔内执行特定的任务或代码块。
时间间隔:指的是两个时间点之间的时间段或持续时间。
例如:日常生活中的时间间隔:
早晨8点到中午12点的时间间隔:指从早晨8点到中午12点这个时间段。
周一到周五的时间间隔:指从周一到周五这段连续的时间,在这个时间间隔内工作或上学。
在项目启动类加上@EnableScheduling注解开启基于注解的定时任务支持。
@EnableScheduling
@EnableAsync
@SpringBootApplication
public class SpringtaskApplication {
public static void main(String[] args) {
SpringApplication.run(SpringtaskApplication.class, args);
}
}
创建一个ScheduledTaskService业务类。
@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 注解:使用该注解标记一个方法,指示Spring在后台定期执行该方法。 //fixedRate 属性:指定任务执行的固定速率,即两次任务开始执行的时间间隔。在本例中为每60秒执行一次任务 @Scheduled(fixedRate = 60000) public void scheduledTaskImmediately() throws InterruptedException { System.out.printf("fixedRate第%s次执行,当前时间为:%s%n", count1++, dateFormat.format(new Date())); Thread.sleep(10000); } //@Scheduled 注解:用于指示Spring框架执行定时任务的方法。 //fixedDelay 属性:确定两次任务的开始时间间隔,以确保前一个任务完成后的固定延迟时间。 @Scheduled(fixedDelay = 60000) public void scheduledTaskAfterSleep() throws InterruptedException { System.out.printf("fixedDelay第%s次执行,当前时间为:%s%n", count2++, dateFormat.format(new Date())); Thread.sleep(10000); } /* fixedRate和fixedDelay的区别: fixedRate:固定执行 到时间了就执行。如果执行时间超过了定任务的时长会有任务重叠的情况发生。 fixedDelay:延迟执行,到时间了不一定执行。下一个任务的启动会等待前一个任务的执行完毕,并且会考虑前一个任务的执行时间。 */ //@Scheduled(cron = "0 * * * * *") 是用于配置基于 Cron 表达式的定时任务的注解. /* * 字段含义: 第一位:秒(0-59) 第二位:分钟(0-59) 第三位:小时(0-23) 第四位:日期天/月(1-31) 第五位:月份(1-12) 第六位:星期(1-7) * */ @Scheduled(cron = "0 * * * * *")//每分钟执行 public void scheduledTaskCron() { System.out.println(String.format("cron第%s次执行,当前时间为:%s", count3++, dateFormat.format(new Date()))); } }
输出结果:
邮件任务通常指的是通过编程自动化发送邮件的任务或功能。
用途:
在pom.xml文件中添加邮件服务的依赖启动器,用于添加Spring Boot邮件支持的依赖配置。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
在全局配置文件中添加邮件服务配置
qq邮箱授权码申请帮助
#发件人邮服务器相关配置
spring.mail.host=smtp.qq.com
spring.mail.port=587
#配置个人QQ账户和密码(密码是加密后的授权码)
spring.mail.username=260*****01@qq.com
spring.mail.password=vmdvjbmtrblrdjfa
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
新建一个邮件发送任务管理的业务处理类SendEmailService,编写了一个发送纯文本邮件的sendSimpleEmail()方法。
@Service public class SendEmailService { @Autowired //邮件发送对象 //初始化JavaMailSenderImpl的时候自动读取密码 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(); } } }
创建测试方法调用sendSimpleEmail方法发送邮件。
@SpringBootTest
class SpringtaskApplicationTests {
@Autowired
private SendEmailService sendEmailService;
@Test
public void sendSimpleMailTest() {
String to = "260*****01@qq.com";
String subject = "【测试邮件任务】标题";
String text = "Spring Boot纯文本邮件发送内容测试.....";
sendEmailService.sendSimpleEmail(to, subject, text);
}
}
成功发送邮件。
编写一个发送带附件和图片邮件的sendComplexEmail()方法。
public void sendComplexEmail(String to, String subject, String text, String filePath, String rscId, String rscPath) { // 创建一个MimeMessage 对象,用于构建复杂的 MIME(Multipurpose Internet Mail Extensions)类型的电子邮件 /*MIME(Multipurpose Internet Mail Extensions)类型是一种标准的互联网媒体类型 * 一些常见的MIME类型包括: * text/plain: 普通文本文件 * text/html: HTML文件 * image/jpeg, image/png, image/gif: 图片文件 * application/pdf: PDF文件 * application/json: JSON数据 * application/xml: XML文件 * audio/mpeg: MP3音频文件 * video/mp4: MP4视频文件 * multipart/form-data: 用于HTML表单中上传文件数据的类型 * */ MimeMessage message = mailSender.createMimeMessage(); try { //MimeMessageHelper是一个用来处理复杂MIME消息的辅助类 //参数 true 的作用是指示创建的 MimeMessageHelper 对象将处理一个多部分的消息(multipart message)。这意味着该消息包含了附件、内嵌资源或者多个部分,而不仅仅是简单的文本邮件。 MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(from); // 发件人 helper.setTo(to); // 收件人 helper.setSubject(subject); // 标题 helper.setText(text, true); // text: 表示要设置的邮件文本内容。 //true: 表示邮件文本内容是否包含HTML格式。当第二个参数设置为true时,表示文本内容将被视为HTML格式的内容;反之,如果设置为false,那么文本内容将被视为普通的纯文本格式。 FileSystemResource res = new FileSystemResource(new File(rscPath));//这个 File 对象封装为一个 FileSystemResource,使其可以作为邮件中的资源使用。 helper.addInline(rscId, res);//向邮件中添加内嵌资源(inline resources) rscId 用于唯一标识内嵌资源 可以cid:rscId 的形式来引用这个内嵌资源 FileSystemResource file = new FileSystemResource(new File(filePath)); String fileName = filePath.substring(filePath.lastIndexOf(File.separator)); // 获取最后一次出现/的下标值 System.out.println("File.separator = " + File.separator); System.out.println("fileName = " + fileName); //向邮件中添加附件 //fileName: 这是要作为附件添加到邮件中的文件名,也是接收方将看到的附件文件名。 //file: 这是表示要添加为附件的文件对象。 helper.addAttachment(fileName, file); mailSender.send(message); System.out.println("复杂邮件发送成功"); } catch (MessagingException e) { System.out.println("复杂邮件发送失败 " + e.getMessage()); e.printStackTrace(); } }
创建测试方法调用sendComplexEmailTest方法发送邮件。
public void sendComplexEmailTest() {
String to = "260*****01@qq.com";
String subject = "【复杂邮件】标题";
StringBuilder text = new StringBuilder();
text.append("<html><head></head>");
text.append("<body><h1>springboot邮件任务!</h1>");
String rscId = "img001";
text.append("<img src='cid:" + rscId + "'/></body>");
text.append("</html>");
String rscPath = "D:\\email\\springboot.png";
String filePath = "D:\\email\\邮件任务.txt";
sendEmailService.sendComplexEmail(to, subject, text.toString(),
filePath, rscId, rscPath);
}
成功发送邮件。
添加Thymeleaf模板引擎依赖启动器。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
在templates目录下创建一个emailTemplate_vercode.html文件。
<!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> 先生/女士,您好:
</div>
<P style="text-indent: 2em">您的新用户验证码为
<span th:text="${code}" style="color: cornflowerblue">123456</span>,请妥善保管。
</P>
</body>
</html>
在SendEmailService类创建sendTemplateEmail方法。
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(); } }
创建测试方法调用sendTemplateEmailTest()。
@Autowired //引用模板引擎的实例 //目的是加载html文件并使用 private TemplateEngine templateEngine; @Test public void sendTemplateEmailTest() { String to = "260*****01@qq.com"; String subject = "【模板邮件】标题"; //创建Context 对象用于将数据传递给模板引擎 //获取模板的方式发送邮件 //获取到模板内容 Context context = new Context(); // 设置值 context.setVariable("username", "石头"); context.setVariable("code", "456123"); // 把数据传入到emailTemplate_vercode模板文件生成邮件内容的文本或HTML。 String emailContent = templateEngine.process("emailTemplate_vercode", context); sendEmailService.sendTemplateEmail(to, subject, emailContent); }
成功发送邮件。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。