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(...)
相关推荐
咩咩啃树皮8 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
鱟鲥鳚8 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
大模型码小白10 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司10 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地11 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
wuqingshun31415912 小时前
TCP超时重传机制是为了解决什么问题?
java
莫逸风14 小时前
【AgentScope 2.0】 0. 学习指南
java·llm·agent·agentscope
隔窗听雨眠14 小时前
Spring Boot在云原生时代的编程范式革新研究
spring boot·后端·云原生
z1234567898615 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
yaoxin52112315 小时前
470. Java 反射 - Member 接口与 AccessFlag
java·开发语言·python