当前位置: 首页 > news >正文

Springboot定时任务

@Component
@EnableScheduling
public class SpringBootTestJob {@Scheduled(cron = "0/5 * * * * ?")public void testScheduled(){System.out.println("SpringBootTestJob test");}
}

这段代码使用了 Spring Boot 自带的定时任务机制。解释如下:

  • @Component:这是一个 Spring 的注解,表示将该类标识为 Spring 容器中的一个 Bean,让 Spring 自动管理它的生命周期。

  • @EnableScheduling:启用 Spring 的调度任务功能。它会在 Spring 容器中创建一个 TaskScheduler,使得 @Scheduled 注解的任务能够按照指定的时间调度运行。

  • @Scheduled(cron = “0/5 * * * * ?”):这是定时任务的核心注解。cron 表达式定义了任务执行的时间规则:

    • 0/5 * * * * ? 表示每隔 5 秒执行一次任务。
      • 0/5:从第 0 秒开始,每 5 秒执行一次;
      • *:每分钟都执行;
      • *:每小时都执行;
      • *:每天都执行;
      • *:每月都执行;
      • ?:不指定星期几(因为月份和星期几的参数冲突时使用 ?)。

这段代码每隔 5 秒打印一次 "SpringBootTestJob test"

适用场景:
  • Spring Boot 内置的定时任务功能适合一些简单的定时任务,尤其是单个节点环境下。
  • 它适合不需要复杂调度、持久化、分布式支持等的场景。

2. 为什么有时候不用 Spring Boot 自带的定时任务?

虽然 Spring Boot 自带的定时任务使用方便,但有一些局限性,尤其是在更复杂的生产环境中,可能无法满足一些需求:

1. 只适合单体,不适合集群:
  • Spring Boot 自带的定时任务在单节点上很好用,但是在集群环境中,所有节点都可能执行相同的定时任务,从而导致任务重复执行,造成问题。
  • 解决方法:可以通过加分布式锁等方式来避免重复执行,但这种方式实现起来相对复杂,且不够灵活。
2. 无法实时更改定时任务状态和策略:
  • 如果你需要动态地停止、修改、重新调度任务,Spring Boot 自带的定时任务就显得不够灵活了。
  • 比如,如果在生产环境中需要临时停止某个定时任务,Spring Boot 的 @Scheduled 注解方式并不容易实现动态控制。
3. 无法持久化任务信息:
  • Spring Boot 自带的定时任务没有持久化机制,如果应用重启,所有定时任务会丢失。虽然可以利用数据库或其他持久化方式实现,但它本身不提供这些功能。

3. Quartz 框架的介绍

Quartz 是一个功能强大的作业调度框架,可以解决上述 Spring Boot 自带定时任务的不足,尤其在集群、持久化、灵活性等方面表现优异。

引入 Quartz 依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

Spring Boot 提供了对 Quartz 的支持,只需要引入这个依赖,框架会自动配置一些 Quartz 的基础设置。

配置 Quartz 定时任务:
@Configuration
public class QuartzConfig {/*** 声明一个任务* @return*/@Beanpublic JobDetail jobDetail() {return JobBuilder.newJob(TestJob.class).withIdentity("TestJob", "test").storeDurably()  // 表示即使没有触发器,任务也会被持久化.build();}/*** 声明一个触发器,什么时候触发这个任务* @return*/@Beanpublic Trigger trigger() {return TriggerBuilder.newTrigger().forJob(jobDetail()).withIdentity("trigger", "trigger").startNow()  // 立即开始.withSchedule(CronScheduleBuilder.cronSchedule("*/2 * * * * ?"))  // 每两秒执行一次.build();}
}
  • JobDetail:表示要执行的作业,即 Quartz 任务类。使用 JobBuilder 创建一个作业实例并指定标识符。
  • Trigger:表示触发器,控制何时执行这个作业。CronScheduleBuilder 用来根据 Cron 表达式设定定时规则。
Quartz Job 类:
#禁用并发执行
@DisallowConcurrentExecution
public class TestJob implements Job {@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {System.out.println("TestJob TEST开始");// 模拟任务执行System.out.println("TestJob TEST结束");}
}
  • @DisallowConcurrentExecution:表示任务不允许并发执行,即在任务没有完成之前,不会再次执行这个任务。
  • JobExecutionContext:上下文信息,包含了任务的所有执行信息。
让quartz将定时调度的任务保存到数据库中

1. 创建数据库表

首先,在数据库中创建相关的 Quartz 表来存储调度任务的状态、触发器和其他相关信息。以下是创建表的 SQL 语句:

-- Quartz 默认的数据库表
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;-- 创建 QRTZ_JOB_DETAILS 表
CREATE TABLE QRTZ_JOB_DETAILS (SCHED_NAME VARCHAR(120) NOT NULL COMMENT '定时任务名称',JOB_NAME VARCHAR(200) NOT NULL COMMENT 'job名称',JOB_GROUP VARCHAR(200) NOT NULL COMMENT 'job组',DESCRIPTION VARCHAR(250) NULL COMMENT '描述',JOB_CLASS_NAME VARCHAR(250) NOT NULL COMMENT 'job类名',IS_DURABLE VARCHAR(1) NOT NULL COMMENT '是否持久化',IS_NONCONCURRENT VARCHAR(1) NOT NULL COMMENT '是否非同步',IS_UPDATE_DATA VARCHAR(1) NOT NULL COMMENT '是否更新数据',REQUESTS_RECOVERY VARCHAR(1) NOT NULL COMMENT '请求是否覆盖',JOB_DATA BLOB NULL COMMENT 'job数据',PRIMARY KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)
);-- 创建 QRTZ_TRIGGERS 表
CREATE TABLE QRTZ_TRIGGERS (SCHED_NAME VARCHAR(120) NOT NULL COMMENT '定时任务名称',TRIGGER_NAME VARCHAR(200) NOT NULL COMMENT '触发器名称',TRIGGER_GROUP VARCHAR(200) NOT NULL COMMENT '触发器组',JOB_NAME VARCHAR(200) NOT NULL COMMENT 'job名称',JOB_GROUP VARCHAR(200) NOT NULL COMMENT 'job组',DESCRIPTION VARCHAR(250) NULL COMMENT '描述',NEXT_FIRE_TIME BIGINT(13) NULL COMMENT '下一次触发时间',PREV_FIRE_TIME BIGINT(13) NULL COMMENT '前一次触发时间',PRIORITY INTEGER NULL COMMENT '等级',TRIGGER_STATE VARCHAR(16) NOT NULL COMMENT '触发状态',TRIGGER_TYPE VARCHAR(8) NOT NULL COMMENT '触发类型',START_TIME BIGINT(13) NOT NULL COMMENT '开始时间',END_TIME BIGINT(13) NULL COMMENT '结束时间',CALENDAR_NAME VARCHAR(200) NULL COMMENT '日程名称',MISFIRE_INSTR SMALLINT(2) NULL COMMENT '未触发实例',JOB_DATA BLOB NULL COMMENT 'job数据',PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME, JOB_NAME, JOB_GROUP) REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME, JOB_NAME, JOB_GROUP)
);-- 其他表的创建省略...

这些表会用来存储定时任务的详细信息、触发器信息等。

2. 配置 Scheduler 使用数据库存储

为了让 Quartz 使用数据库来保存任务调度信息,我们需要在 application.propertiesapplication.yml 文件中进行相应配置。如下所示:

# Quartz 配置,使用 JDBCJobStore 存储任务
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = quartzDataSource
org.quartz.jobStore.tablePrefix = QRTZ_# 数据源配置
spring.datasource.url=jdbc:mysql://localhost:3306/quartz_db?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.initial-size=5
spring.datasource.druid.min-idle=5
spring.datasource.druid.max-active=20

这些配置使得 Quartz 使用 MySQL 数据库并将调度信息保存到数据库中。

3. 配置 JobFactoryScheduler

在 Spring Boot 项目中,Quartz 使用 SchedulerFactoryBean 来创建和配置调度器。你可以通过自定义 JobFactory 来注入 Spring 管理的 Bean。以下是增强后的 MyJobFactorySchedulerConfig 类:

@Component
public class MyJobFactory extends SpringBeanJobFactory {@Resourceprivate AutowireCapableBeanFactory beanFactory;@Overrideprotected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {Object jobInstance = super.createJobInstance(bundle);beanFactory.autowireBean(jobInstance);  // 注入 Spring 管理的依赖return jobInstance;}
}@Configuration
public class SchedulerConfig {@Resourceprivate MyJobFactory myJobFactory;@Beanpublic SchedulerFactoryBean schedulerFactoryBean(@Qualifier("dataSource") DataSource dataSource) throws IOException {SchedulerFactoryBean factory = new SchedulerFactoryBean();factory.setDataSource(dataSource);factory.setJobFactory(myJobFactory);  // 配置 JobFactory,注入 Spring Beanfactory.setStartupDelay(2);  // 延迟启动调度器return factory;}
}

通过这种方式,我们可以确保 Quartz 任务的执行能够访问到 Spring 容器中的所有 Bean。

4. 创建任务调度接口

创建一个 JobController 来操作定时任务的增加、暂停、恢复、删除等操作。以下是增强后的 JobController 类:

@RestController
@RequestMapping(value = "/admin/job")
public class JobController {private static Logger LOG = LoggerFactory.getLogger(JobController.class);@Autowiredprivate SchedulerFactoryBean schedulerFactoryBean;@RequestMapping(value = "/add")public CommonResp add(@RequestBody CronJobReq cronJobReq) {String jobClassName = cronJobReq.getName();String jobGroupName = cronJobReq.getGroup();String cronExpression = cronJobReq.getCronExpression();String description = cronJobReq.getDescription();LOG.info("创建定时任务开始:{},{},{},{}", jobClassName, jobGroupName, cronExpression, description);CommonResp commonResp = new CommonResp();try {Scheduler sched = schedulerFactoryBean.getScheduler();sched.start();  // 启动调度器JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(jobClassName)).withIdentity(jobClassName, jobGroupName).build();CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName, jobGroupName).withDescription(description).withSchedule(scheduleBuilder).build();sched.scheduleJob(jobDetail, trigger);} catch (SchedulerException | ClassNotFoundException e) {LOG.error("创建定时任务失败:" + e);commonResp.setSuccess(false);commonResp.setMessage("创建定时任务失败: " + e.getMessage());}LOG.info("创建定时任务结束:{}", commonResp);return commonResp;}// 其他接口:run、pause、resume、delete等
}

5. 请求参数和返回结果

请求参数 CronJobReq 类:

public class CronJobReq {private String group;private String name;private String description;private String cronExpression;// Getter 和 Setter 方法
}

返回结果 CronJobResp 类:

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class CronJobResp {private String group;private String name;private String description;private String state;private String cronExpression;@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private Date nextFireTime;@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private Date preFireTime;// Getter 和 Setter 方法
}

6. 测试接口

你可以使用以下 HTTP 请求来测试定时任务的创建:

POST http://localhost:8000/admin/job/add
Content-Type: application/json{"name": "com.stu.train.batch.job.TestJob","group": "default","cronExpression": "*/2 * * * * ?","description": "Test job"
}

通过上述步骤,Quartz 将会将调度任务的信息保存到数据库中,确保任务的持久化管理和恢复。

相关文章:

Springboot定时任务

Component EnableScheduling public class SpringBootTestJob {Scheduled(cron "0/5 * * * * ?")public void testScheduled(){System.out.println("SpringBootTestJob test");} }这段代码使用了 Spring Boot 自带的定时任务机制。解释如下&#xff1a; …...

node.js知识点总结

1、Node.js Node. js是一个基于 Chrome v8引擎的服务器端 JavaScript运行环境&#xff1b;Node. js是一个事件驱动、非阻塞式I/O的模型&#xff0c;轻量而又高效&#xff1b;Node. js的包管理器npm是全球最大的开源库生态系统。 2、数据处理中的buffer&#xff1a; 具体…...

Kotlin中泛型的协变

interface Shapeclass Circle : Shapefun main() {val shapes1: List<Shape> listOf<Circle>()val shapes2: MutableList<Shape> mutableListOf<Circle>() }如上代码&#xff0c;第一行赋值语句是OK的&#xff0c;第二行赋值语句在编辑器上直接就报错…...

第三百二十五节 Java线程教程 - Java Fork/Join框架

Java线程教程 - Java Fork/Join框架 fork/join框架通过利用机器上的多个处理器或多个内核来解决问题。 该框架有助于解决涉及并行性的问题。 fork/join框架创建一个线程池来执行子任务。 当线程在子任务上等待完成时&#xff0c;框架使用该线程来执行其他线程的其他未决子任…...

网络游戏安全现状及相关应对方案

中国网络游戏历经十余年的飞速发展&#xff0c;取得了显著成就&#xff0c;但与此同时&#xff0c;也陷入了诸多安全问题的泥沼。 一、中国网络游戏发展中的安全困境 &#xff08;一&#xff09;灰色产业链滋生 外挂、私服、盗号、打金工作室以及网络信息诈骗等灰色产业链在…...

uniapp h5地址前端重定向跳转

简单说下功能&#xff0c;就是在地址输入http://localhost:8080/home 会自行跳转到http://localhost:8080/pages/home/index&#xff0c;如果有带参数的话也会携带上去。 ps&#xff1a;只能在h5中使用 首先需要用到query-string 安装query-string npm install query-string…...

uniapp隐藏自带的tabBar

uniapp隐藏自带的tabBar 场景: 微信小程序在使用自定义tabBar组件时, 隐藏uniapp自带的tabBar <template> <!-- index页面 --> </template> <script setup> import { onShow } from /utils/wxUtils onShow(() > {uni.hideTabBar() // 隐藏自带的tab…...

使用--log-file保存pytest的运行日志

前面使用了tee和重定向来保存pytest的运行日志&#xff0c;这次使用--log-file&#xff0c;因为它可以配置日志的级别、格式和每行日志的生成时间。 pytest -q -s -ra --count100 test_open_stream.py --alluredir./report/CXL --log-filepytest_log.txt 【pytest.ini】 使用…...

WebAPI性能监控-MiniProfiler与Swagger集成

Net8_WebAPI性能监控-MiniProfiler与Swagger集成 要在.NET Core项目中集成MiniProfiler和Swagger&#xff0c;可以按照以下步骤操作&#xff1a; 安装NuGet包&#xff1a; 安装MiniProfiler.AspNetCore.Mvc包以集成MiniProfiler。安装MiniProfiler.EntityFrameworkCore包以监…...

视频会议接入GB28181视频指挥调度,语音对讲方案

传统的视频会议指挥调度系统目前主流的互联网会议大部分都是私有协议&#xff0c;功能都很独立。目前主流的视频监控国标都最GB平台&#xff0c;新的需求要求融合平台要接入监控等设备&#xff0c;并能实现观看监控接入会议&#xff0c;实时语音设备指挥现场工作人员办公实施。…...

深度学习和图像处理

看来你对深度学习和图像处理很感兴趣呢&#xff0c;让我来一一解答你的疑惑吧。 深度学习高纬度特征 首先&#xff0c;我猜你是想问“深度学习中的高维特征”吧。在深度学习中&#xff0c;随着网络层数的加深&#xff0c;网络的感受野逐渐变大&#xff0c;语义表达能力也随之增…...

〔 MySQL 〕数据类型

目录 1.数据类型分类 2 数值类型 2.1 tinyint类型 2.2 bit类型 2.3 小数类型 2.3.1 float 2.3.2 decimal 3 字符串类型 3.1 char 3.2 varchar 3.3 char和varchar比较 4 日期和时间类型 5 enum和set mysql表中建立属性列&#xff1a; 列名称&#xff0c;类型在后 n…...

云安全之云计算基础

0x00 前言 本文主要是针对云计算相关的基础梳理和整理。 云计算 NIST 800-145ISO/IEC 17788ISO/IEC 17789云安全 NIST 500-299 云安全ISO / IEC FDIS 27017 云安全0x01 云计算基础 什么是云计算: 一种新的运作模式和一组用于管理计算资源共享池的技术。云计算是一种颠覆性的…...

PostgreSQL pg-xact(clog)目录文件缺失处理

一、 背景 前些天晚上突然收到业务反馈&#xff0c;查询DB中的一个表报错 Could not open file "pg-xact/005E": No such file or directory. 两眼一黑难道是文件损坏了...登录查看DB日志&#xff0c;还好没有其他报错&#xff0c;业务也反馈只有这一个表在从库查询报…...

《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明

参考 《element plus 使用 icon 图标(两种方式)》使用 icon 升级 Vue2 升级 Vue3 项目时&#xff0c;遇到命名时的实心与空心点差异&#xff01; ElementUI&#xff1a; 实心是 el-icon-more空心是 el-icon-more-outline ElementPlus&#xff1a; 实心是 el-icon-more-fill…...

基于碎纸片的拼接复原算法及MATLAB实现

一、问题描述 破碎文件的拼接在司法物证复原、历史文献修复以及军事情报获取等领域都有着重要的应用。传统上&#xff0c;拼接复原工作需由人工完成&#xff0c;准确率较高&#xff0c;但效率很低。特别是当碎片数量巨大&#xff0c;人工拼接很难在短时间内完成任务。随着计算…...

苍穹外卖 软件开发流程

软件开发的流程&#xff1a; 1.需求分析 完成需求规格说明书、产品原型。 需求规格说明书&#xff1a;一般而言是word文档描述当前项目的各个组成部分&#xff0c;如&#xff1a;系统定义、应用环境、功能规格、性能需求等&#xff0c;都会在文档中描述。 …...

mysqldump导出表结构和表数据和存储过程和函数

0、查看表结构信息 (1) 只查看表结构的注释信息 select table_name,table_comment from information_schema.tables where table_schema 表所在的库 and table_name 表名 &#xff1b; mysql> select table_name,table_comment from information_schema.tables where tabl…...

常见的排序算法及分类对比

虽然在竞赛和编程语言中用到的排序算法主要是时间复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn) 的高效算法,但作为算法学习,我们要从简单到复杂,认识常见的排序算法,并理解其算法思想。本文列出几乎所有的排序算法并进行分类对比。 排序算法总表 以下是一个对比表格…...

多窗口切换——selenium

获取窗口句柄&#xff08;以Python Selenium为例&#xff09; current_window_handle方法 用于获取当前窗口的句柄。句柄是一个标识符&#xff0c;用于唯一标识一个窗口。示例代码&#xff1a; from selenium import webdriverdriver webdriver.Chrome() driver.get("…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号&#xff08;第三种&#xff09;后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...