【SpringBoot详细教程】-12-SpringBoot整合定时任务 【持续更新】

news2024/11/30 1:48:20

        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","默默","早上好,今天又是元气满满的一天");
}

效果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2215591.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python实现文本数据可视化:构建动态词云

引言 在信息爆炸的时代&#xff0c;如何有效地从海量的文本数据中提取关键信息并直观展示&#xff0c;成为数据分析师和研究人员面临的重要挑战。词云作为一种流行的文本可视化工具&#xff0c;通过不同大小、颜色和字体的文字展示文本中关键词的出现频率或重要性&#xff0c;…

<<迷雾>> 第11章 全自动加法计算机(8)--一只开关取数并相加 示例电路

用一只开关就可完成将所有的数从存储器里依次取出并逐个相加的过程. info::操作说明 增加了 指令寄存器 和 译码电路, 扩充了 RR 循环移位寄存器 存储器中前 10 个地址已经提前写入了指令和数值, 其中 17(10001) 代表装载指令, 18(10010) 代表相加指令, 其它则为要加的数. 需检…

vue3--通用 button 组件实现

背景 在日常开发中,我们一般都是利用一些诸如:element-ui、element-plus、ant-design等组件库去做我们的页面或者系统 这些对于一些后台管理系统来说是最好的选择,因为后台管理系统其实都是大同小异的,包括功能、布局结构等 但是对于前台项目,比如官网、门户网站这些 …

StableDiffusion|833种艺术家风格项目,提示词直接上手! AI绘画文生图直接抄!

大家好&#xff0c;我是画画的小强 众所周知&#xff0c;Stable Diffusion是一个强大的文生图模型&#xff0c;能够根据用户的文本描述生成高质量的图像。在这个过程中&#xff0c;提示词&#xff08;Prompt&#xff09;的选择和构造具有至关重要的作用。提示词是向模型描述你…

RPA好用吗?RPA机器人如何使用?

数字化飞速发展的时代&#xff0c;企业越来越追求效率和成本控制&#xff0c;以期在激烈的市场竞争中保持领先地位。在此背景下&#xff0c;RPA机器人流程自动化作为一种能够提升业务流程效率的先进技术&#xff0c;成为助力企业数字化转型和高质量发展的强劲助力。那么&#x…

【前端】制作一个自己的网页(4)

刚才我们完成了网页中标题与段落元素的学习。在实际开发时&#xff0c;一个网页通常会包含多个相同元素&#xff0c;比如多个标题与段落。 对于相同标签的元素&#xff0c;我们又该如何区分定位呢&#xff1f; 对多个相同的标签分类 比如右图设置了七个段落元素&#xff0c;它…

Android ImageView scaleType使用

目录 一、src设置图片资源 二、scaleType设置图片缩放类型 三、scaleType具体表现 matrix&#xff1a; fitXY: fitStart&#xff1a; fitCenter&#xff1a; fitEnd: Center&#xff1a; centerCrop: centerInside&#xff1a; 控制ImageView和图片的大小保持一致…

实例详解 | 借助 Langchain 和 Gemma 2 构建 RAG 应用

本文将为您介绍如何使用 LangChain、NestJS 和 Gemma 2 构建关于 PDF 格式 Angular 书籍的 RAG 应用。接着&#xff0c;HTMX 和 Handlebar 模板引擎将响应呈现为列表。应用使用 LangChain 及其内置的 PDF 加载器来加载 PDF 书籍&#xff0c;并将文档拆分为小块。然后&#xff0…

【Golang】合理运用泛型,简化开发流程

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

测试用例如何进行评估?4个指标

测试用例评估是确保软件测试活动能够达到预期目标的关键步骤。评估测试用例的有效性和质量&#xff0c;对于确保软件产品的质量和可靠性至关重要。如果未对测试用例进行评估&#xff0c;可能导致测试用例不完整、不准确或无效&#xff0c;进而引发需求遗漏、测试不充分等问题&a…

241015_把一个文件夹中的所有图片按照序列命名

241015_把一个文件夹中的所有图片按照序列命名(数据集重命名) 在数据集制作过程中&#xff0c;我们经常会遇到合并图片数据集后命名格式不统一或者因重复命名自动添加&#xff08;1&#xff09;&#xff08;2&#xff09;的问题&#xff0c;以下是一段代码&#xff0c;对合并后…

中国研究员使用量子计算机破解 RSA 加密

由上海大学的 Wang Chao 领导的研究团队发现&#xff0c;D-Wave 的量子计算机可以优化问题解决&#xff0c;从而可以攻击 RSA 等加密方法。 中国研究人员公布了一种使用 D-Wave 的量子退火系统来破解经典加密的方法&#xff0c;这可能会加快量子计算机对广泛使用的加密系统构成…

推荐系统架构

推荐系统架构 推荐和搜索系统核心的的任务是从海量物品中找到用户感兴趣的内容。在这个背景下&#xff0c;推荐系统包含的模块非常多&#xff0c;每个模块将会有很多专业研究的工程和研究工程师&#xff0c;作为刚入门的应届生或者实习生很难对每个模块都有很深的理解&#xf…

鼠标右键删除使用Visual Studio 打开(v)以及恢复【超详细】

鼠标右键删除使用Visual Studio 打开&#xff08;v&#xff09; 1. 引言2. 打开注册表3. 进入对应的注册表地址4. 右键删除 AnyCode 项5. 效果6. 备份注册表文件——恢复菜单 1. 引言 安装完 Visual Studio 鼠标右键总有 “使用Visual Studio 打开(v)”&#xff0c;让右键菜单…

windows修改文件最后修改时间

一、需要修改日期的文件 背景&#xff1a;有时候我们需要做一些文件定期删除的操作&#xff0c;但是测试时候并不一定有符合测试的文件&#xff0c;这时候就需要可以方便的修改文件的最后修改时间。 系统环境&#xff1a;windows 测试文件&#xff1a;如上 修改时间方式&#x…

Linux网络编程(三)-UDP协议及网络通信详解

1.UDP协议 概念&#xff1a; 除了 TCP 协议外&#xff0c;还有 UDP 协议&#xff0c;想必大家都听过说&#xff0c;UDP 是 User Datagram Protocol 的简称&#xff0c;中文名是用户数据报协议&#xff0c;是一种无连接、不可靠的协议&#xff0c;同样它也是工作在传顺层。它只…

基于FreeRTOS的LWIP移植

目录 前言一、移植准备工作二、以太网固件库与驱动2.1 固件库文件添加2.2 库文件修改2.3 添加网卡驱动 三、LWIP 数据包和网络接口管理3.1 添加LWIP源文件3.2 Lwip文件修改3.2.1 修改cc.h3.2.2 修改lwipopts.h3.2.3 修改icmp.c3.2.4 修改sys_arch.h和sys_arch.c3.2.5 修改ether…

利用Python filestream实现文件流读

在 Python 中&#xff0c;文件流&#xff08;filestream&#xff09;操作通过内置的 open() 函数实现&#xff0c;它提供了对文件的读取、写入、以及流控制的支持。常见的文件模式包括&#xff1a; r&#xff1a;只读模式&#xff08;默认&#xff09;。w&#xff1a;写入模式…

用Python构建动态折线图:实时展示爬取数据的指南

背景/引言 随着大数据和人工智能的不断发展&#xff0c;实时数据分析变得越来越关键&#xff0c;尤其是在金融市场中。股市数据的实时可视化可以帮助投资者快速做出决策&#xff0c;避免错失良机。Python 凭借其强大的数据处理能力和丰富的可视化库&#xff0c;成为分析和展示…

你不是算法工程师,就可以不了解AI大模型技术吗?

身处人工智能的大浪潮之中&#xff0c;除了算法工程师&#xff0c;其他的角色也都应当对人工智能大模型技术有一定的了解。所以&#xff0c;笔者将针对“什么是人工智能&#xff1f;”“非技术人员对于人工智能大模型的理解存在哪些门槛&#xff1f;”等问题与大家分享自己的见…