第九章springboot任务管理
阅读准备
在学习赛博学习法之后,应用到学习中。
学习本章的目的:学习这个知识,希望我能读懂企业开发的项目在什么时候用到任务管理,并且知道实现的思路,熟悉具体实现。
看到标题产生的联想:之前看过相关文章,任务管理是不是设计你在什么时候完成某个任务,或者在什么时候清除某些任务,在特定的时间实现登录操作等,就比如你待在系统中5分钟啦,系统会安排个任务给你,让你进行验证。我之前你跑美团的时候就需要这样子,上线30分钟,让你刷一次脸,检验是不是本人操作。这个是不是有关于任务管理的。
阅读大概的文章我觉得比较重要的内容:
作者认为熟悉springboot整合异步,定时,邮件任务是本章的重点。应该企业中只用到这些吧,可能是我们写的时候用的少,让我们熟悉一下基本流程,到时候再项目实战中能够使用出来。
本章的标题:
异步任务
1.分类:有返回值和无返回值的异步任务调用。
对无返回值的异步任务进行整和实现讲解,
有返回值的异步任务进行整合的实现讲解。
定时任务:故名思意,就是在特定的时间设计一个任务,在那个时间做那个任务。
先介绍定时任务,介绍定时任务需要用到的注解,并且讲解它相关属性,再根据怎么实现进行讲解。
邮件任务:
这个任务就是实现发送邮件给用户的。
邮件需要发送只有文本的邮件,发送附带邮件和图片的,发送模板邮件(模板邮件是不是又有图片又有文章的结合)
作者告诉我们需要亲自实现案例代码!!!
在大概了解文章之后,我个人的总结:
略读本章内容之后,我对每种任务在什么情况下使用有啦简单的了解,异步任务有关多线程的知识,定时任务比较好理解,邮件发送任务,在本章感觉会是比较难搞定的,这个任务就是实现比如数据报表之后,对其他用户进行邮件发送,发送数据过去。在网站上比较常用。
问题四:看一下作者接下来讲什么内容。
确定一下这个内容的专业问题:
它的作用是什么,
它能用在什么地方
它是怎么实现的,具体的思路是怎么样的
确定我的个人问题
假如我是程序员,我该怎么使用它
它与什么内容有点类似?
正式阅读
导读:在web应用中,任务的调度(?)能提高系统的效率。这里的任务就包括定时,异步(?),发邮件任务。
9.1 异步任务
导读:在web应用中,对于数据交互的处理,有三种方式来处理:
1.同步方式(响应迟缓),2. 多线程,3.异步调用。
我们接下来讲解异步调用的两种方式:
9.1.1 无返回值的异步调用
使用到的场景:给新用户发送验证码。
实现步骤:
1.创建springboot项目:springboot继承啦spring框架的异步任务的支持,所以只需要建立简单的springboot项目就可以啦。
2.编写异步调用方法
在service包下编写模拟用户短信验证码发送的方法:
//模拟发送短信
@Service
public class MyAsyncService {
@Async//将方法标记为异步方法
public void shendSMS() throws Exception{
System.out.println("调用短信验证码业务方法...");
Long startTime = System.currentTimeMillis();
Thread.sleep(5000);
Long endTime = System.currentTimeMillis();
System.out.println("短信业务执行完成耗时:"+(endTime-startTime));
}
}
这个代码使用到新的注解,作用是把方法标记为异步方法。
3.开启基于注解的异步任务支持
想让上一步标记的异步方法生效,还要再启动类开启异步任务支持。
使用到
@EnableAsync进行标注基于注解的异步任务支持
@EnableAsync
@SpringBootApplication
public class Springbootchapt9Application {
public static void main(String[] args) {
SpringApplication.run(Springbootchapt9Application.class, args);
}
}
4.编写控制层业务调用方法
再controller包下,编写调用的方法:
@RestController
public class MyAsyncController {
@Autowired
private MyAsyncService myAsyncService;
@GetMapping("/sendSMS")
public String shendSMS() throws Exception{
Long startTime = System.currentTimeMillis();
myAsyncService.shendSMS();
Long endTime = System.currentTimeMillis();
System.out.println("主流程耗时:"+(endTime-startTime));
return "success";
}
}
测试结果发现,先执行调用的无返回值的异步任务,它虽然执行,但是它没有执行完,会继续向下一步执行,所以测试结果是,先执行完成主流程的任务,再完成异步任务的结果。
这个时候异步任务就好比是另外其啦一个线程,不影响主线程的流程,它自己也会慢慢的执行完。
9.1.2 有返回值异步任务调用
场景:一个程序调用两个业务方法,并获取这两个业务方法的结果,进行汇总。
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);//封装返回的异步结果信息为Future<Integer>类型
}
@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);//封装返回的异步结果信息为Future<Integer>类型
}
2.编写控制层业务调用方法
@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 endTime = System.currentTimeMillis();
System.out.println("主流程耗时:"+(endTime-startTime));
return "success";
}
测试之后成功。
通过测试我们知道:有返回值的异步任务,在汇总它们的返回结果的时候,主流程会等待结果出来之后才继续执行下去,也就是出现啦短暂的阻塞,在这短暂的阻塞异步异步任务作为子线程并行执行的(也就是一起执行)。等到子线程的执行结果出现之后,主流程才会继续执行下去并返回结果。
9.2 定时任务
用到这类任务的场景:在某个固定或者每隔一段时间让程序去执行某一个任务。
我们通常可以使用spring框架的Scheduling Tasks实现定时任务的处理。
定时任务调度功能实现的两种实现方式:配置和注解的方式。
相关的注解:
1.@EnableScheduling
用于开启定时任务的支持,用在项目启动类类上。
2.@Scheduled
作用:配置定时任务的执行规则。
用在定时业务方法上。
这个注解有多个属性,用于精细化定时任务的规则。
接下来对下面的属性进行讲解:
第一个属性的使用示例:
从左到右的位数分别表示:秒,分,时,日,月,星期。
*表示任意时刻,MON-FRI表示周一到周五。
每一位都有丰富的阻字段值:
上表是cron属性每一位可以取的值,也可以取特殊字符的值。
对应特殊字符的介绍:
这里讲到cron表达式的赋值有些不能用在cron属性中,cron表达式我不太清楚是什么,应该也是定时任务中的某些东西把。cron表达式和cron属性是不同的两个东西。
第二个属性zome:
这个属性指定时区的,默认情况下不用到,也就是你要指定那个区域的时区,北京时间还是纽约时间。
第三个和第四个属性:
它们的作用类似,只是属性值不同,一个是long类型,一个是数值字符串类型。它们作用就是,上一次的定时任务执行完之后,隔一段时间,就继续执行下一次任务。
也就类似我做完一次作业,休息5分钟,下一次再继续做作业。
d第5,第六个属性:
它们与上两个属性的作用类似,不同的是:它们的记时方式不同:3,4属性是从上一次任务完成开始计时。5,6属性是在上一次任务开始执行的时候开始计时。
di7,8属性:
这两个属性的作用类似,用于延时第一个定时任务的执行。
就比如:你在第一次写作业前,你先拖延那几分钟,然后再去写作业。
这两个属性可以跟前面,3456属性进行配合使用。
9.2.2 定时任务实现
1.再service编写定时任务管理的业务处理类:
private static final SimpleDateFormat date
= 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++, date.format(new Date())));
}
@Scheduled(fixedDelay = 6000)//模拟该定时任务处理耗时10秒
public void scheduledTaskAfterSleep() throws InterruptedException {
System.out.println(String.format("fixedDate第%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.测试结果:
private static final SimpleDateFormat date
= 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++, date.format(new Date())));
}
@Scheduled(fixedDelay = 6000)//模拟该定时任务处理耗时10秒,启动项目这个方法立即执行,在上一次任务执行完之后再执行一次
public void scheduledTaskAfterSleep() throws InterruptedException {
System.out.println(String.format("fixedDate第%s次执行,当前时间为:%s"
,count2++, date.format(new Date())));
Thread.sleep(10000);
}
@Scheduled(cron = "0 * * * * *")//在整数分钟时间点首次执行。每隔一分钟重复执行一次
public void scheduledTaskCron(){
System.out.println(String.format("cron第%s次执行,当前时间为:%s"
,count3++, date.format(new Date())));
}
这里知道啦定时任务该怎么设置啦,并且知道它们的时间设置规则和什么时候开始计算时间,这里的定时任务就是再经过一段时间后,再次执行这个方法。
9.3 邮件任务
早期开发人员实现邮件功能使用的是javaMail相关的api,后来spring推出相关的技术进行整合简化它,springboot就对spring提出的邮件发送服务进行啦整合支持。
9.3.1 发送纯文本邮件
纯文本定制:只用指定收件人邮箱账号,邮件标题,邮件内容就可以啦。
实现整合步骤:
1.添加邮件服务依赖启动器
在发送邮件任务书写时,直接用spring提供的javaMailSender接口或者它的实现类javaMailSenderImpl
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2.添加邮件服务配置
在配置文件中添加邮件服务相关的配置,确保邮件正常发送
#发件人邮箱的主机和端口号,每种邮箱的主机和端口号都不同。
spring.mail.host=smtp.qq.com
spring.mail.port=587
spring.mail.username=2965613845@qq.com
#加密后的授权码
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
4.编写邮件发送服务:
在service包中编写对应的类:
//这个类的编写就是编写邮件的内容,然后借用JavaMailSenderImpl的send方法实现邮件的发送。
@Service
public class SendEmailService {
@Autowired
private JavaMailSenderImpl mailSender;
@Value("${spring.mail.username}")
private String from;
public void sendEmail(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();
}
}
}
在测试类中调用这个方法并把内容添加进去:
@Autowired
private SendEmailService sendEmailService;
@Test
public void sendEmailtest() {
String to="2965613845@qq.com";
String subject="村文本标题";
String text="springboot的邮件发送内容测试";
sendEmailService.sendEmail(to,subject,text);
}
9.3.2 发送带附件和图片的邮件
我们讲解如何发送附件或者图片的邮件:
1.定制附件和图片的邮件发送服务:
/**
* 发送复杂邮件(包括静态资源和附件)
* @param to 收件人地址
* @param subject 邮件标题
* @param text 邮件内容
* @param filePath 附件地址
* @param rscId 静态资源唯一标识
* @param rscPath 静态资源地址
*
*/
public void sendComplexEmail(String to,String subject,String text,String filePath,String rscId,String rscPath){
// 定制复杂邮件信息MimeMessage
MimeMessage message = mailSender.createMimeMessage();
try {
// 使用MimeMessageHelper帮助类,并设置multipart多部件使用为true
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="2127269781@qq.com";
String subject="【复杂邮件】标题";
// 定义邮件内容
StringBuilder text = new StringBuilder();
//编辑邮件内容使用html标签
text.append("<html><head></head>");
text.append("<body><h1>祝大家元旦快乐!</h1>");
// cid为固定写法,rscId指定一个唯一标识
String rscId = "img001";
text.append("<img src='cid:" +rscId+"'/></body>");
text.append("</html>");
// 指定静态资源文件和附件路径
String rscPath="F:\\email\\newyear.jpg";
String filePath="F:\\email\\元旦放假注意事项.docx";
// 发送复杂邮件
sendEmailService.sendComplexEmail(to,subject,text.toString(),filePath,rscId,rscPath);
}
这里也就说明啦,我们发送邮件的时候可以设置内容为html的内容,然后发送到别人的邮件上。
9.3.3 发送模板邮件
上面都是我们自己手动制定邮件的内容,针对一些用户注册邮件等场景,我们可以定制一些通用 的模板邮件进行邮件发送。
思路:
1.添加Thymeleaf模板依赖然后编写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>
这里面有两个动态的变量:username和code,这两个值与后台进行交互的时候才填充。
2.编写邮件发送服务,把数据以及html内容整合成邮件以及发送邮件的操作:
/**
* 发送模板邮件
* @param to 收件人地址
* @param subject 邮件标题
* @param content 邮件内容
*/
public void sendTemplateEmail(String to, String subject, String content) {
MimeMessage message = mailSender.createMimeMessage();
try {
// 使用MimeMessageHelper帮助类,并设置multipart多部件使用为true
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();
}
}
3.设置数据传到实现类里面:
@Autowired
private SendEmailService sendEmailService;
@Autowired
private TemplateEngine templateEngine;//模板引擎解析器
@Test
public void sendTemplateEmailTest() {
//定制发送模板邮件需要的参数
String to="2127269781@qq.com";
String subject="【模板邮件】标题";
// 使用模板邮件定制邮件正文内容
Context context = new Context();//使用这个对象对html模板中的username和code动态赋值
context.setVariable("username", "石头");
context.setVariable("code", "456123");
// 使用TemplateEngine设置要处理的模板页面
String emailContent = templateEngine.process("emailTemplate_vercode", context);//模板解析器中的方法,用来解析模板,第一个参数是要解析的模板页面,第二个设置动态数据
// 发送模板邮件
sendEmailService.sendTemplateEmail(to,subject,emailContent);
}
这里就可以设置成功啦。
上面演示的是一次一个收件人,如果是一个人想发送给多个收件人,只需要设置收件人的地址为字符串类型的数组就可啦。
这里已经把所有内容给讲解完啦。
原文地址:https://blog.csdn.net/laiyoumi/article/details/144212778
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!