SpringBoot 3.5.4 整合Quartz 定时任务

Quartz和@Scheduled对比

@Scheduled适合简单固定定时任务,不能动态新增,不能暂停/恢复定时任务,不能动态删除定时任务

下面是一个简单的SpringBoot+Quartz示例

依赖

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>demo</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <!-- Web 开发 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- AOP,用于实现自定义注解功能 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <!-- 测试依赖,Spring Boot 3.x 用这个 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

         <!--  Quartz依赖  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

写个JOB

复制代码
package com.example.demo.job;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.time.LocalDateTime;
//具体要干什么
@DisallowConcurrentExecution//用于 例如任务要执行20分钟  但是5分钟执行一次  防止重复
public class HelloJob extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        System.out.println("Quartz 定时任务执行:" + LocalDateTime.now());
    }
}

配置 JobDetail 和 Trigger

复制代码
package com.example.demo.config;

import com.example.demo.job.HelloJob;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration//配置 JobDetail 和 Trigger
public class QuartzConfig {
    /**
     * helloJobDetail:定义一个任务,任务类是 HelloJob
     * helloJobTrigger:定义触发器,每 5 秒执行一次 helloJob
     * @return
     */
    @Bean
    public JobDetail helloJobDetail() {
        return JobBuilder.newJob(HelloJob.class)
                .withIdentity("helloJob")
                .storeDurably()
                .build();
    }

    @Bean
    public Trigger helloJobTrigger() {
        return TriggerBuilder.newTrigger()
                .forJob(helloJobDetail())
                .withIdentity("helloJobTrigger")
                //有多种写法
                .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?"))
                .build();
    }
}

上面代码就是固定5秒执行一次System.out.println("Quartz 定时任务执行:" + LocalDateTime.now());

和简单定时任务没有什么区别

如果希望JOB中注入Service

复制代码
@Service
public class OrderService {

    public void syncOrder() {
        System.out.println("同步订单:" + LocalDateTime.now());
    }
}

定时执行Service中的任务

复制代码
package com.example.demo.job;

import com.example.demo.service.OrderService;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.time.LocalDateTime;
//具体要干什么
public class HelloJob extends QuartzJobBean {
    private final OrderService orderService;
    public HelloJob(OrderService orderService) {
        this.orderService = orderService;
    }
    @Override
    protected void executeInternal(JobExecutionContext context) {
        orderService.syncOrder();
    }
}

注意Quartz要引入spring-boot-starter-jdbc

和application.yaml

这样会自动创建相关表 当然如果你不引入spring-boot-starter-jdbc也没事 就是相关quartz的表不会自动创建 和Mybatis不起冲突 不配置application.yaml也没关系 默认内存中

复制代码
spring:
  application:
    name: demo



  datasource:
    url: jdbc:mysql://127.0.0.1:3306/demo_quartz?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8
    username: root
    password: xxxxxxxxx
    driver-class-name: com.mysql.cj.jdbc.Driver
  quartz:
    job-store-type: jdbc
    jdbc:
      initialize-schema: always

 <!--  Quartz依赖  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

上面代码和简单定时任务没什么两样 反而简单定时任务更加方便

Quartz 真正强的地方是:

运行时动态新增任务

运行时修改 cron

运行时暂停任务

运行时恢复任务

运行时删除任务

任务信息可以存数据库

后台页面可以管理任务

Quartz理解 就是说任务代码是写死的 可以动态增删查 启动 暂停 恢复 但是不能改()

可以动态改"调度规则"
不能直接动态改"Java 代码逻辑"

Quartz 默认不是动态改代码。
Quartz 是动态管理任务调度。 当然可以实现动态改代码 但是不建议

示例:

一个喊话功能

{

"jobName": "testJob",

"jobGroup": "default",

"cron": "0/5 * * * * ?",

"message": "hello quartz"

}

写一个通用JOB

message 是创建任务时动态传进来的

复制代码
package com.example.demo.job;

import org.quartz.JobExecutionContext;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.time.LocalDateTime;

public class DynamicJob extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext context) {
        String message = context.getMergedJobDataMap().getString("message");

        System.out.println("动态 Quartz 任务执行");
        System.out.println("时间:" + LocalDateTime.now());
        System.out.println("参数:" + message);
    }
}

写一个通用 Job

这个 Job 不写死任务名,也不写死执行时间。

复制代码
复制代码
package com.example.demo.job;

import org.quartz.JobExecutionContext;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.time.LocalDateTime;

public class DynamicJob extends QuartzJobBean {

    @Override
    protected void executeInternal(JobExecutionContext context) {
        String message = context.getMergedJobDataMap().getString("message");

        System.out.println("动态 Quartz 任务执行");
        System.out.println("时间:" + LocalDateTime.now());
        System.out.println("参数:" + message);
    }
}

这里重点是:

复制代码
复制代码
context.getMergedJobDataMap().getString("message")

这个 message 是创建任务时动态传进来的。


写请求 DTO

复制代码
复制代码
package com.example.demo.dto;

public class QuartzJobDTO {

    private String jobName;
    private String jobGroup;
    private String cron;
    private String message;

    public String getJobName() {
        return jobName;
    }

    public void setJobName(String jobName) {
        this.jobName = jobName;
    }

    public String getJobGroup() {
        return jobGroup;
    }

    public void setJobGroup(String jobGroup) {
        this.jobGroup = jobGroup;
    }

    public String getCron() {
        return cron;
    }

    public void setCron(String cron) {
        this.cron = cron;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

写 QuartzService

核心就是这个类。

复制代码
复制代码
package com.example.demo.service;

import com.example.demo.dto.QuartzJobDTO;
import com.example.demo.job.DynamicJob;
import org.quartz.*;
import org.springframework.stereotype.Service;

@Service
public class QuartzService {

    private final Scheduler scheduler;

    public QuartzService(Scheduler scheduler) {
        this.scheduler = scheduler;
    }

    /**
     * 新增定时任务
     */
    public void addJob(QuartzJobDTO dto) throws SchedulerException {

        JobKey jobKey = JobKey.jobKey(dto.getJobName(), dto.getJobGroup());

        if (scheduler.checkExists(jobKey)) {
            throw new RuntimeException("任务已存在:" + dto.getJobName());
        }

        JobDetail jobDetail = JobBuilder.newJob(DynamicJob.class)
                .withIdentity(jobKey)
                .usingJobData("message", dto.getMessage())
                .storeDurably()
                .build();

        TriggerKey triggerKey = TriggerKey.triggerKey(dto.getJobName() + "Trigger", dto.getJobGroup());

        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .forJob(jobDetail)
                .withSchedule(CronScheduleBuilder.cronSchedule(dto.getCron()))
                .build();

        scheduler.scheduleJob(jobDetail, trigger);
    }

    /**
     * 暂停任务
     */
    public void pauseJob(String jobName, String jobGroup) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复任务
     */
    public void resumeJob(String jobName, String jobGroup) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        scheduler.resumeJob(jobKey);
    }

    /**
     * 删除任务
     */
    public void deleteJob(String jobName, String jobGroup) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        scheduler.deleteJob(jobKey);
    }

    /**
     * 修改 cron 表达式
     */
    public void updateCron(String jobName, String jobGroup, String newCron) throws SchedulerException {

        TriggerKey triggerKey = TriggerKey.triggerKey(jobName + "Trigger", jobGroup);

        CronTrigger newTrigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .withSchedule(CronScheduleBuilder.cronSchedule(newCron))
                .build();

        scheduler.rescheduleJob(triggerKey, newTrigger);
    }

    /**
     * 立即执行一次
     */
    public void runOnce(String jobName, String jobGroup) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        scheduler.triggerJob(jobKey);
    }
}

重点看这几个方法:

复制代码
复制代码
scheduler.scheduleJob(jobDetail, trigger);   // 新增任务
scheduler.pauseJob(jobKey);                  // 暂停任务
scheduler.resumeJob(jobKey);                 // 恢复任务
scheduler.deleteJob(jobKey);                 // 删除任务
scheduler.rescheduleJob(triggerKey, trigger);// 修改执行时间
scheduler.triggerJob(jobKey);                // 立即执行一次

这些就是 Quartz 真正有用的地方。


写 Controller

复制代码
复制代码
package com.example.demo.controller;

import com.example.demo.dto.QuartzJobDTO;
import com.example.demo.service.QuartzService;
import org.quartz.SchedulerException;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/quartz")
public class QuartzController {

    private final QuartzService quartzService;

    public QuartzController(QuartzService quartzService) {
        this.quartzService = quartzService;
    }

    @PostMapping("/add")
    public String add(@RequestBody QuartzJobDTO dto) throws SchedulerException {
        quartzService.addJob(dto);
        return "新增任务成功";
    }

    @PostMapping("/pause")
    public String pause(String jobName, String jobGroup) throws SchedulerException {
        quartzService.pauseJob(jobName, jobGroup);
        return "暂停任务成功";
    }

    @PostMapping("/resume")
    public String resume(String jobName, String jobGroup) throws SchedulerException {
        quartzService.resumeJob(jobName, jobGroup);
        return "恢复任务成功";
    }

    @PostMapping("/delete")
    public String delete(String jobName, String jobGroup) throws SchedulerException {
        quartzService.deleteJob(jobName, jobGroup);
        return "删除任务成功";
    }

    @PostMapping("/updateCron")
    public String updateCron(String jobName, String jobGroup, String cron) throws SchedulerException {
        quartzService.updateCron(jobName, jobGroup, cron);
        return "修改 cron 成功";
    }

    @PostMapping("/runOnce")
    public String runOnce(String jobName, String jobGroup) throws SchedulerException {
        quartzService.runOnce(jobName, jobGroup);
        return "立即执行成功";
    }
}

测试新增任务

用 Postman 请求:

复制代码
复制代码
POST http://localhost:8080/quartz/add
Content-Type: application/json

请求体:

复制代码
复制代码
{
  "jobName": "testJob",
  "jobGroup": "default",
  "cron": "0/5 * * * * ?",
  "message": "hello quartz"
}

控制台每 5 秒输出:

复制代码
复制代码
动态 Quartz 任务执行
时间:2026-06-04T...
参数:hello quartz

暂停任务

复制代码
复制代码
POST http://localhost:8080/quartz/pause?jobName=testJob&jobGroup=default

任务停止执行。


恢复任务

复制代码
复制代码
POST http://localhost:8080/quartz/resume?jobName=testJob&jobGroup=default

任务继续执行。


修改执行时间

比如改成每 10 秒执行一次:

复制代码
复制代码
POST http://localhost:8080/quartz/updateCron?jobName=testJob&jobGroup=default&cron=0/10 * * * * ?

删除任务

复制代码
复制代码
POST http://localhost:8080/quartz/delete?jobName=testJob&jobGroup=default

任务被删除。


这才是 Quartz 和 @Scheduled 的区别

@Scheduled 是这样:

复制代码
复制代码
@Scheduled(cron = "0/5 * * * * ?")
public void test() {
    System.out.println("执行任务");
}

问题是:

复制代码
复制代码
cron 写死在代码里
任务写死在代码里
不能方便地从数据库加载
不能方便地后台新增
不能方便地后台暂停
不能方便地后台删除

Quartz 是这样:

复制代码
复制代码
任务名、cron、状态、参数可以放数据库
Spring Boot 启动时从数据库读取任务
然后动态注册到 Scheduler
后台页面可以动态管理任务

真实项目一般怎么设计

真实项目一般会建一张表:

复制代码
复制代码
CREATE TABLE sys_job (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    job_name VARCHAR(100),
    job_group VARCHAR(100),
    job_class VARCHAR(255),
    cron_expression VARCHAR(100),
    params VARCHAR(1000),
    status TINYINT,
    remark VARCHAR(500)
);

数据示例:

复制代码
复制代码
id: 1
job_name: syncOrderJob
job_group: default
job_class: com.example.demo.job.SyncOrderJob
cron_expression: 0 */5 * * * ?
params: {"type":"order"}
status: 1

后台页面展示:

复制代码
复制代码
任务名称        cron              状态      操作
同步订单        0 */5 * * * ?      启用      暂停 / 修改 / 删除 / 执行一次
清理日志        0 0 2 * * ?        启用      暂停 / 修改 / 删除 / 执行一次

然后后端根据数据库内容调用:

复制代码
复制代码
scheduler.scheduleJob(...)
scheduler.pauseJob(...)
scheduler.resumeJob(...)
scheduler.deleteJob(...)
scheduler.rescheduleJob(...)
相关推荐
mifengxing1 小时前
LeetCode热题100——字母异位词分组
java·算法·leetcode·职场和发展·哈希表·hot100
微三云、小叶2 小时前
排队免单系统底层设计:四种分配算法拆解,无预支资金的合规营销架构方案
java·前端·软件开发·商业模式·本地生活·商业思维
就叫_这个吧2 小时前
Java+MySQL+Mybatis+Junit4实现学生信息管理系统
java·mysql·mybatis
乐之者v2 小时前
xxl-job添加执行器
java
可乐ea2 小时前
【知识获取与分享社区项目 | 项目日记第 19 天】基于 Elasticsearch 实现关键词检索与业务权重排序
java·大数据·spring boot·mysql·elasticsearch·搜索引擎·全文检索
用户398346161203 小时前
Go-Spring 实战第 18 课 —— App 使用:启动、配置与运行期扩展
spring·go
zzz_23683 小时前
【Spring】面试突击系列(一):IoC 与 DI 深度解析
java·spring·面试
于先生吖3 小时前
前后端分离体育服务项目,场馆计费+线下赛事排行小程序部署开发教程
java·小程序·uni-app
RemainderTime3 小时前
Spring Boot脚手架集成 Spring Security实现生产级RBAC鉴权
spring boot·后端·spring