Hello,大胸弟们,我们又又又见面了,今天攀哥继续为大家分享一下SpringBoot的教程,没点关注的宝宝,点一下关注。
🌲 定时任务简介
🌿 定时任务使用场景
我们在编写SpringBoot应⽤中经常会遇到这样的场景,⽐如:我需要定时地发送⼀些短信、邮件之类 的操作,也可能会定时地检查和监控⼀些标志、参数等。
比如需要在每天凌晨的时候,分析一次前一天的日志信息,Spring为我们提供了异步执行任务调度的方式。
定时任务在实际项目开发中很常见,并且定时任务可以在各种场景中应用,通过自动化操作和任务的规模化管理,提高效率、可靠性和工作质量。可以减少手动操作,避免疏忽和错误,并节省时间和人力资源的投入 。
🌿 定时任务的优点
- 简单易用:使用注解驱动的方式,简化了定时任务的配置和管理。通过添加@Scheduled注解,可以将普通的方法标记为定时任务,而不需要手动编写定时任务调度的代码。
- 内置任务调度器:Spring Boot内置了一个轻量级的任务调度器,可以方便地执行定时任务。它提供了线程池、任务管理和并发处理等功能,可以高效地管理和执行任务。
- 灵活的配置选项:@Scheduled注解支持各种配置选项,例如cron表达式、fixedRate、fixedDelay等,可以非常灵活地定义任务的触发时间和频率。这使得开发人员能够根据具体需求精确控制定时任务的执行方式
🌲 定时任务的使用
🌿 简单案例
SpringBoot为我们提供了@EnableScheduling 和 @Scheduled两个注解来实现定时任务,用法比较简单:
- 在启动器上添加注解@EnableScheduling开启定时任务
package com.moxuan.boot_03_async;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableAsync
@EnableScheduling
public class Boot03AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(Boot03AsyncApplication.class, args);
}
}
- 创建一个java类作为定时任务的类,然后再在类中写上任务方法,在方法上加上注解@Scheduled
@Scheduled注解中的属性值:
- fixedRate: 固定频率执行,单位毫秒
- fixedDelay: 延迟执行,单位毫秒
- cron: cron表达式,指定表达式所表示的时间执行
package com.moxuan.boot_03_async.jobs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 定时任务
*
* 第一位,表示秒,取值0-59
* 第二位,表示分,取值0-59
* 第三位,表示小时,取值0-23
* 第四位,日期天/日,取值1-31
* 第五位,日期月份,取值1-12
* 第六位,星期,取值1-7,1表示星期天,2表示星期一
* 第七位,年份,可以留空,取值1970-2099
*
*/
@Component
public class HeartbeatJob {
private static final Logger logger = LoggerFactory.getLogger(HeartbeatJob.class);
/**
* 检查状态1
*/
@Scheduled(cron = "0 35 14 * * ?")
public void checkState1() {
logger.info(">>>>> cron中午14:35上传检查开始....");
logger.info(">>>>> cron中午14:35上传检查完成....");
}
int count =0;
/**
* 检查状态2
*/
// @Scheduled(fixedRate = 3000)
public void checkState2() {
count += 1;
logger.info(">>>>> fixedRate 每隔3秒检查....当前检查第"+count+"次");
}
// @Scheduled(fixedDelay = 5000)
public void checkStat3(){
logger.info(">>>>> fixedDelay 这是一个5秒后执行的....");
}
}
🌿 cron 表达式
Cron 表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron 有如下两种语法格式:
- 秒 分 小时 日期 月份 星期 年
- 秒 分 小时 日期 月份 星期
👇 各字段的含义:
字段 | 允许值 | 允许的特殊字符 |
秒(Seconds) | 0~59的整数 | , - * / 四个字符 |
分(Minutes) | 0~59的整数 | , - * / 四个字符 |
小时(Hours) | 0~23的整数 | , - * / 四个字符 |
日期(DayofMonth) | 1~31的整数(但是你需要考虑你月的天数) | ,- * ? / L W C 八个字符 |
月份(Month) | 1~12的整数或者 JAN-DEC | , - * / 四个字符 |
星期(DayofWeek) | 1~7的整数或者 SUN-SAT (1=SUN) | , - * ? / L C # 八个字符 |
年(可选,留空)(Year) | 1970~2099 | , - * / 四个字符 |
每一个域都可以使用数字,也还可以出现如下特殊字符,它们的含义分别是:
- * :表示匹配该域的任意值,假如在Minutes域使用 *, 即表示每分钟都会触发事件。
- ?:只能用在 DayofMonth 和 DayofWeek 两个域。它也匹配域的任意值,但实际不会。因为 DayofMonth 和 DayofWeek 会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用 ?,而不能使用 *,如果使用 * 表示不管星期几都会触发,实际上并不是这样。
- -:表示范围,例如在 Minutes 域使用 5-20,表示从5分到20分钟每分钟触发一次
- /:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用 5/20, 则意味着从第 5 分钟触发一次,然后每隔 20 分钟触发一次
- ,:表示列出枚举值值。例如:在 Minutes 域使用 5,20,则意味着在5和20分每分钟触发一次。
- L:表示最后,只能出现在 DayofWeek 和 DayofMonth 域,如果在 DayofWeek 域使用 5L,意味着在最后的一个星期四触发。
- W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth使用 5W,如果 5 日是星期六,则将在最近的工作日:星期五,即 4 日触发。如果 5 日是星期天,则在6日(周一)触发;如果 5 日在星期一 到星期五中的一天,则就在 5 日触发。另外一点,W 的最近寻找不会跨过月份。
- LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。
- #:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如 4#2 ,表示某月的第二个星期三。
✍ 举几个例子:
- 每隔5秒执行一次:*/5 * * * * ?
- 每隔1分钟执行一次:0 */1 * * * ?
- 每天23点执行一次:0 0 23 * * ?
- 每天凌晨1点执行一次:0 0 1 * * ?
- 每月1号凌晨1点执行一次:0 0 1 1 * ?
- 每月最后一天23点执行一次:0 0 23 L * ?
- 每周星期天凌晨1点实行一次:0 0 1 ? * L
- 在26分、29分、33分执行一次:0 26,29,33 * * * ?
- 每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
- 表示在每月的1日的凌晨2点调度任务:0 0 2 1 * ? *
- 表示周一到周五每天上午10:15执行作业:0 15 10 ? * MON-FRI
- 表示2002-2006年的每个月的最后一个星期五上午10:15执行:0 15 10 ? 6L 2002-2006
🌲 定时邮件发送
🌿 发送邮件的步骤
邮件发送,在我们的日常开发中,也非常的多,Springboot 帮我们做了支持:
- 邮件发送需要引入 spring-boot-start-mail
- SpringBoot 自动配置 MailSenderAutoConfiguration
- 定义 MailProperties 内容,配置在 application.yml 中
- 自动装配 JavaMailSender
- 测试邮件发送
首先引入 pom 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
然后需要我们在全局配置文件中配置 spring.mail:
# 你的邮箱地址
spring.mail.username=XXXXXX@qq.com
# 你的QQ授权码
spring.mail.password=XXXXXX
# 邮箱服务器
spring.mail.host=smtp.qq.com
# qq需要配置ssl
spring.mail.properties.mail.smtp.ssl.enable=true
获取授权码:在QQ邮箱中的 设置 -> 账户 -> 开启 pop3 和 smtp 服务
发送简单的文本邮件,在util包下新建一个EmailUtil类,专用来发送邮件(注意在EmailUtil类上添加@Component注解 ):
@Autowired
private JavaMailSender javaMailSender;
/**
* 向用户邮箱发送短信
*
* @param email 收件人邮箱
*/
public void sendEmailMessage(String email) {
MimeMessage message = javaMailSender.createMimeMessage();
try {
//获取验证码 存入redis
//邮箱发送内容组成
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setSubject("【墨轩学习网】愿世间美好与您紧紧相拥");
helper.setText("早上好!我喜欢你很久了", true);
helper.setTo(email);
helper.setFrom("墨轩学习网" + '<' + "1032974410@qq.com" + '>');
javaMailSender.send(message);
logger.info("邮件发送成功!!");
} catch (MessagingException e) {
logger.error("邮件发送失败!!");
}
}
在定时任务中发送邮件
@Autowired
EmailUtil emailUtil;
@Scheduled(cron = "0 47 22 * * ?")
public void sendEmail(){
emailUtil.sendEmailMessage("278421824@qq.com");
}
上面的案例中虽然可以发送邮件,但是发送的简单的文本,在实际开发中往往需要按照某种格式的邮件方式去发送,比如以html的方式发送给客户,接下来我们看看如何按照html模板发送邮件:
在项目中新建ftl目录,在其中新建邮件模板:mail.ftl
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="email code">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<!--邮箱验证码模板-->
<body>
<div style="background-color:#ECECEC; padding: 35px;">
<table cellpadding="0" align="center"
style="width: 800px;height: 100%; margin: 0px auto; text-align: left; position: relative; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-size: 14px; font-family:微软雅黑, 黑体; line-height: 1.5; box-shadow: rgb(153, 153, 153) 0px 0px 5px; border-collapse: collapse; background-position: initial initial; background-repeat: initial initial;background:#fff;">
<tbody>
<tr>
<th valign="middle"
style="height: 25px; line-height: 25px; padding: 15px 35px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: RGB(148,0,211); background-color: RGB(148,0,211); border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px;">
<font face="微软雅黑" size="5" style="color: rgb(255, 255, 255); ">【墨轩学习网】愿世间再无难学的技术</font>
</th>
</tr>
<tr>
<td style="word-break:break-all">
<div style="padding:25px 35px 40px; background-color:#fff;opacity:0.8;">
<h2 style="margin: 5px 0px; ">
<font color="#333333" style="line-height: 20px; ">
<font style="line-height: 22px; " size="4">
尊敬的用户:</font>{0}
</font>
</h2>
<!-- 中文 -->
<p>您好!感谢您使用墨轩学习网,{1}</p><br>
<div style="width:100%;margin:0 auto;">
<div style="padding:10px 10px 0;border-top:1px solid #ccc;color:#747474;margin-bottom:20px;line-height:1.3em;font-size:12px;">
<p>墨轩学习网研发团队</p>
<p>联系我们:1032974410@qq.com</p>
<br>
<p>此为系统邮件,请勿回复<br>
</p>
<!--<p>©***</p>-->
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
编写邮箱工具类:
package com.moxuan.boot_03_async.util;
import com.moxuan.boot_03_async.jobs.HeartbeatJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.MessageFormat;
@Component
public class EmailUtil {
// 日志记录员
private static final Logger logger = LoggerFactory.getLogger(EmailUtil.class);
/**
* 读取邮件模板
* 替换模板中的信息
*
* @param ftl 需要发送的模板
* @return
*/
public String buildContent(String ftl,Object ... objects) {
//加载邮件html模板
Resource resource = new ClassPathResource(ftl);
InputStream inputStream = null;
BufferedReader fileReader = null;
StringBuffer buffer = new StringBuffer();
String line = "";
try {
inputStream = resource.getInputStream();
fileReader = new BufferedReader(new InputStreamReader(inputStream));
while ((line = fileReader.readLine()) != null) {
buffer.append(line);
}
} catch (Exception e) {
logger.info("发送邮件读取模板失败{}", e);
} finally {
if (fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//替换html模板中的参数
return MessageFormat.format(buffer.toString(), objects);
}
@Autowired
private JavaMailSender javaMailSender;
/**
* 向用户邮箱发送短信
*
* @param email 收件人邮箱
*/
public void sendEmailMessage(String email,String ftl,Object ... obj) {
MimeMessage message = javaMailSender.createMimeMessage();
try {
//获取验证码 存入redis
//邮箱发送内容组成
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setSubject("【墨轩学习网】愿世间美好与您紧紧相拥");
helper.setText(buildContent(ftl,obj), true);
helper.setTo(email);
helper.setFrom("墨轩学习网" + '<' + "1032974410@qq.com" + '>');
javaMailSender.send(message);
logger.info("邮件发送成功!!");
} catch (MessagingException e) {
logger.error("邮件发送失败!!");
}
}
}
在定时任务中发送邮件
@Autowired
EmailUtil emailUtil;
@Scheduled(cron = "0 34 22 * * ?")
public void sendEmail(){
emailUtil.sendEmailMessage("278421824@qq.com","ftl/mail.ftl","默默","早上好,今天又是元气满满的一天");
}
效果如下: