杰瑞科技汇

Java定时任务Quartz如何配置与执行?

目录

  1. Quartz 核心概念
  2. 快速入门:一个简单的 "Hello World" 示例
  3. 进阶:使用 JobDataMap 传递数据
  4. 高级:使用 Cron 表达式
  5. Spring/Spring Boot 集成(推荐方式)
  6. 总结与最佳实践

Quartz 核心概念

在开始编码前,理解 Quartz 的几个核心组件至关重要:

Java定时任务Quartz如何配置与执行?-图1
(图片来源网络,侵删)
  • Job (作业)

    • 这是一个接口,你只需要实现它的 execute() 方法,这个接口代表一个“可执行的任务”,即你想要定时执行的逻辑。
    • 发送邮件、生成报表、清理数据等。
  • JobDetail (作业详情)

    • 它不是 Job 本身,而是 Job 的实例化配置信息,它包含了 Job 的全限定类名、关联的 JobDataMap(用于给 Job 传递数据)以及一些其他元数据。
    • JobDetail 是一个“定义”,而 Job 是一个“执行体”,你可以用同一个 JobDetail 多次调度同一个 Job
  • Trigger (触发器)

    • 它定义了 Job 的执行时间规则。Trigger 决定了 Job 何时、以何种频率执行。
    • Quartz 提供了两种主要的 Trigger
      • SimpleTrigger:用于简单的调度,比如在指定时间执行一次,或每隔 N 秒重复执行 N 次。
      • CronTrigger:功能更强大,基于 Cron 表达式,可以定义复杂的调度规则(如:每个工作日的上午 9 点执行)。
  • Scheduler (调度器)

    Java定时任务Quartz如何配置与执行?-图2
    (图片来源网络,侵删)
    • 这是 Quartz 的核心控制器,它负责将 JobTrigger 绑定在一起,并按照 Trigger 的定义来执行 Job
    • Scheduler 的生命周期是:创建 -> 添加 Job 和 Trigger -> 启动 -> 关闭。

关系图解:

+----------------+      +---------------------+
|   Scheduler    |----->|      Trigger        |
| (调度器)       |      | (触发器: 何时执行)   |
+----------------+      +----------+----------+
        ^                         |
        |                         |
        |                         v
        |                 +----------------+
        |                 |   JobDetail    |
        |                 | (作业详情: 执行什么)|
        |                 +--------+--------+
        |                          |
        |                          |
        |                          v
        |                  +----------------+
        +----------------->|      Job       |
                           | (作业: 具体逻辑) |
                           +----------------+

快速入门:一个简单的 "Hello World" 示例

这个示例将展示如何手动创建一个最简单的定时任务。

步骤 1: 添加依赖

使用 Maven,在你的 pom.xml 中添加 Quartz 依赖:

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version> <!-- 建议使用较新稳定版本 -->
</dependency>
<!-- 不需要单独的 slf4j-api,quartz 会传递依赖 -->

步骤 2: 创建一个 Job

创建一个实现 Job 接口的类。

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 这是你定时任务要执行的具体逻辑
        System.out.println("Hello, Quartz! " + new Date());
    }
}

步骤 3: 编码调度任务

创建一个主类来启动调度器。

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.Date;
public class QuartzSchedulerDemo {
    public static void main(String[] args) throws SchedulerException {
        // 1. 创建一个 SchedulerFactory
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        // 2. 从工厂中获取一个 Scheduler 实例
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 3. 创建 JobDetail 实例,并与 HelloJob 类绑定
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("myJob", "group1") // 给 Job 一个身份标识
                .build();
        // 4. 创建 Trigger 实例,定义立即执行,然后每 5 秒重复一次
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger", "group1") // 给 Trigger 一个身份标识
                .startNow() // 立即生效
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(5) // 每 5 秒
                        .repeatForever()) // 永远重复
                .build();
        // 5. 将 Job 和 Trigger 绑定到 Scheduler 中
        scheduler.scheduleJob(jobDetail, trigger);
        // 6. 启动 Scheduler
        scheduler.start();
        // 为了让程序不退出,保持 Scheduler 运行
        try {
            Thread.sleep(60000); // 运行 60 秒后关闭
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 7. 关闭 Scheduler
        scheduler.shutdown();
    }
}

运行 QuartzSchedulerDemo,你将看到控制台每 5 秒打印一次 "Hello, Quartz!"。


进阶:使用 JobDataMap 传递数据

有时我们需要在执行任务时传递一些参数,比如用户 ID、订单号等,这时就可以使用 JobDataMap

JobDataMapjava.util.Map 的实现,可以在 JobDetailTrigger 中设置。

修改 Job 类

public class DataPassingJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 从 JobExecutionContext 中获取 JobDataMap
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        // 从 map 中获取数据
        String jobName = dataMap.getString("jobName");
        int count = dataMap.getInt("count");
        System.out.println("Job Name: " + jobName + ", Count: " + count + ", Time: " + new Date());
    }
}

修改调度代码

// ... 在 main 方法中 ...
// 3. 创建 JobDetail 并设置数据
JobDetail jobDetail = JobBuilder.newJob(DataPassingJob.class)
        .withIdentity("dataJob", "group1")
        .usingJobData("jobName", "Data Passing Test") // 传递字符串
        .usingJobData("count", 0) // 传递整数
        .build();
// 4. 创建 Trigger,也可以在 Trigger 中设置数据
Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("dataTrigger", "group1")
        .startNow()
        .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(3)
                .repeatForever())
        .usingJobData("triggerParam", "Hello from Trigger!") // 在 Trigger 中设置的数据
        .build();
// ... 其余代码不变 ...

Job 中,你可以通过 context.getTrigger().getJobDataMap() 来获取 Trigger 中设置的数据。


高级:使用 Cron 表达式

CronTrigger 是最常用的触发器,它使用 Cron 表达式来定义复杂的调度时间。

Cron 表达式格式

[秒] [分] [小时] [日] [月] [周] [年]

  • 代表所有值
  • 不指定值,用于日和周字段,避免冲突
  • 范围,如 1-5
  • 列表,如 1,3,5
  • 步长,如 0/5 表示从 0 开始,每 5 秒一次

常用示例:

  • 0 0 12 * * ?:每天中午 12 点触发。
  • 0 15 10 ? * *:每天上午 10:15 触发。
  • 0 0/5 14 * * ?:每天下午 2 点到 2:55 点之间,每 5 分钟触发一次。
  • 0 0/30 9-17 * * ?:每天上午 9 点到下午 5 点之间,每 30 分钟触发一次。
  • 0 0 0 L * ?:每月最后一天午夜触发。
  • 0 0 0 ? * 1-5:每周一至周五的午夜触发。

使用 CronTrigger

// ... 在 main 方法中 ...
// 3. 创建 JobDetail
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
        .withIdentity("cronJob", "group1")
        .build();
// 4. 创建 CronTrigger,设置表达式(每天上午 9 点执行)
Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("cronTrigger", "group1")
        .withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 * * ?")) // 每天 9:00
        .build();
// ... 其余代码不变 ...

Spring/Spring Boot 集成(推荐方式)

在实际企业级应用中,不推荐像上面那样手动创建和调度任务,最佳实践是将其与 Spring/Spring Boot 框架集成,这样可以利用 Spring 的 IoC 容器管理 Bean,实现更优雅的代码。

Spring Boot 集成(非常简单)

步骤 1: 添加依赖

Spring Boot 提供了 spring-boot-starter-quartz,它会自动配置好所有事情。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

步骤 2: 创建 Job

Quartz 的 Job 不能是 Spring 的 @Service,但我们可以让它注入 Spring 管理的 Bean,Quartz 会通过 AutowireCapableBeanJobFactory 自动为你注入。

import org.quartz.*;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class SpringBootJob implements Job {
    // 你可以直接注入任何 Spring 管理的 Bean
    // @Autowired
    // private SomeService someService;
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("Spring Boot 集成的 Quartz Job 正在执行! " + new Date());
        // someService.doSomething();
    }
}

步骤 3: 配置 Job 和 Trigger

创建一个配置类来定义 Job 和 Trigger。

import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuartzConfig {
    // 1. 定义 JobDetail
    @Bean
    public JobDetail springBootJobDetail() {
        // 不需要指定 class,因为 Spring 会根据名字自动查找
        return JobBuilder.newJob(SpringBootJob.class)
                .withIdentity("springBootJob")
                .storeDurably() // 没有立即关联的 Trigger 时需要设置为持久化
                .build();
    }
    // 2. 定义 Trigger
    @Bean
    public Trigger springBootJobTrigger() {
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/10 * * * * ?");
        return TriggerBuilder.newTrigger()
                .forJob(springBootJobDetail()) // 关联 JobDetail
                .withIdentity("springBootJobTrigger")
                .withSchedule(cronScheduleBuilder)
                .build();
    }
}

就这么简单! 启动你的 Spring Boot 应用,Quartz 会自动被初始化,并按照你定义的 Cron 表达式执行任务。


总结与最佳实践

  1. 使用 JobDataMap 传递数据:避免在 Job 类中硬编码参数,使任务更具通用性。
  2. 优先使用 CronTrigger:对于绝大多数业务场景,Cron 表达式比 SimpleTrigger 更灵活、更易读。
  3. 使用 Spring/Spring Boot 集成:在项目中,强烈推荐使用框架集成,它简化了配置,让你能专注于业务逻辑,并能方便地与其他 Spring 组件(如事务、数据源)集成。
  4. 处理 Job 异常:在 Jobexecute 方法中妥善处理异常,避免任务因未捕获的异常而失败,Quartz 会将异常记录下来。
  5. 持久化配置:对于生产环境,务必配置 Quartz 使用数据库(JDBC JobStore)来存储 Job 和 Trigger 的信息,这样即使应用重启,调度任务也不会丢失,并且支持集群部署,Spring Boot Starter 默认会使用内存存储,你需要通过 application.properties 修改为 JDBC 存储。
    # 示例:配置为 JDBC 存储 (需要提前创建 Quartz 相关表)
    spring.quartz.job-store-type=jdbc
    spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate

希望这份详细的指南能帮助你掌握 Quartz 定时任务!

分享:
扫描分享到社交APP
上一篇
下一篇