一、前言

Quartz定时任务框架。Quartz是一个开源的作业调度框架,它允许开发人员根据时间间隔来调度作业。它实现了作业和触发器的多对多关系,还能将多个作业与不同的触发器关联。Quartz的核心是调度器,由它来调度作业。

二、Quartz的主要组成部分

  1. Job(作业):这是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现这个接口来定义需要执行的任务。

  2. JobDetail(作业详情):用来定义Job的实例,包含Job的具体信息,比如Job的类名、组名、以及需要传递的数据等。

  3. Trigger(触发器):用于触发Job的执行。它定义了什么时候执行Job,以及执行的频率。有两种主要的触发器:SimpleTrigger(简单触发器)和CronTrigger(基于Cron表达式的触发器)。

  4. Scheduler(调度器):是Quartz的核心,负责调度Job和Trigger。它从Trigger获取即将要执行的任务,然后执行Job的execute方法。

使用Quartz的一般步骤:

  1. 引入Quartz的依赖(如果是Maven项目,则添加依赖)。

  2. 创建一个实现Job接口的类,重写execute方法,在这里面写需要定时执行的任务。

  3. 创建JobDetail实例,并指定执行的Job类。

  4. 创建Trigger实例,指定触发时间和频率。

  5. 创建Scheduler实例,将JobDetail和Trigger注册到Scheduler中,并启动Scheduler。

下面是一个简单的示例,演示如何使用Quartz每隔5秒执行一次任务。

注意:由于Quartz 2.0之后API有变化,我们使用最新的API。

首先,添加Maven依赖(以Quartz 2.3.2为例):

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
</dependency>

然后,编写一个Job类:

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class SimpleJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("Hello Quartz! " + new java.util.Date());
    }
}

接着,编写调度代码:

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzTest {
    public static void main(String[] args) throws SchedulerException {
        // 1. 创建调度器Scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();

        // 2. 创建JobDetail实例,并与SimpleJob类绑定
        JobDetail jobDetail = JobBuilder.newJob(SimpleJob.class)
                .withIdentity("job1", "group1")
                .build();

        // 3. 创建Trigger实例,每隔5秒重复执行一次
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(5)
                        .repeatForever())
                .build();

        // 4. 将JobDetail和Trigger注册到Scheduler中,并启动
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
    }
}

除了SimpleTrigger,还可以使用CronTrigger,它基于Cron表达式,可以更灵活地定义时间表。例如,每天中午12点执行一次:

Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("trigger2", "group1")
        .withSchedule(CronScheduleBuilder.cronSchedule("0 0 12 * * ?"))
        .build();

注意:在实际项目中,我们通常会将Scheduler配置为Spring的Bean,使用Spring集成的Quartz来管理,这样更方便。但是上面的例子是独立的Quartz应用。

另外,Quartz还支持持久化作业存储(JobStore),可以将作业和触发器信息存储在数据库中,从而支持集群和故障恢复。

三、核心概念

1. 主要组件

// Job - 定义要执行的任务
public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("任务执行时间: " + new Date());
    }
}

// JobDetail - 任务详情
// Trigger - 触发器,定义执行时间
// Scheduler - 调度器,管理任务调度

2. Maven 依赖

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
</dependency>

3. 简单示例

public class QuartzDemo {
    public static void main(String[] args) throws SchedulerException {
        // 1. 创建调度器
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        
        // 2. 创建任务详情
        JobDetail job = JobBuilder.newJob(MyJob.class)
                .withIdentity("myJob", "group1")
                .build();
        
        // 3. 创建触发器
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger", "group1")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(10)  // 每10秒执行一次
                        .repeatForever())
                .build();
        
        // 4. 将任务和触发器注册到调度器
        scheduler.scheduleJob(job, trigger);
        
        // 5. 启动调度器
        scheduler.start();
    }
}

四、触发器类型

1. SimpleTrigger - 简单触发器

// 立即开始,每30秒执行一次,总共执行5次
Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("trigger1", "group1")
        .startNow()
        .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(30)
                .withRepeatCount(4))  // 重复4次,加上第一次共5次
        .build();

// 延迟5秒开始,每1分钟执行一次,无限重复
Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("trigger2", "group1")
        .startAt(DateBuilder.futureDate(5, DateBuilder.IntervalUnit.SECOND))
        .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInMinutes(1)
                .repeatForever())
        .build();

2. CronTrigger - Cron表达式触发器

// 使用Cron表达式
Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("trigger3", "group1")
        .withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?"))  // 每5分钟执行
        .build();

3.常用Cron表达式

"0 0 12 * * ?"          // 每天中午12点
"0 15 10 ? * *"         // 每天上午10:15
"0 0/5 14 * * ?"        // 每天下午2点到2:55期间,每5分钟
"0 0-5 14 * * ?"        // 每天下午2点到2:05期间,每分钟
"0 0/30 8-9 5,20 * ?"   // 每月5号和20号的8点到9点,每30分钟
"0 0 9 ? * MON"         // 每周一上午9点

五、Job数据传递

1. 向Job传递参数

public class DataJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        String name = dataMap.getString("name");
        int count = dataMap.getInt("count");
        
        System.out.println("Name: " + name + ", Count: " + count);
    }
}

// 设置参数
JobDetail job = JobBuilder.newJob(DataJob.class)
        .withIdentity("dataJob", "group1")
        .usingJobData("name", "QuartzJob")
        .usingJobData("count", 1)
        .build();

2. 在Job执行期间更新数据

public class UpdateDataJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        int count = dataMap.getInt("count");
        count++;
        dataMap.put("count", count);
        
        System.out.println("执行次数: " + count);
    }
}

六、持久化配置

1. 数据库持久化

创建 quartz.properties 文件:

# 配置数据源
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = password
org.quartz.dataSource.myDS.maxConnections = 30

# 使用JDBC JobStore
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_

七、Spring Boot 集成

1. 添加依赖

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

2. 配置类

@Configuration
public class QuartzConfig {
    
    @Bean
    public JobDetail sampleJobDetail() {
        return JobBuilder.newJob(SampleJob.class)
                .withIdentity("sampleJob")
                .storeDurably()
                .build();
    }
    
    @Bean
    public Trigger sampleJobTrigger() {
        SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(30)
                .repeatForever();
        
        return TriggerBuilder.newTrigger()
                .forJob(sampleJobDetail())
                .withIdentity("sampleTrigger")
                .withSchedule(scheduleBuilder)
                .build();
    }
}

3. Spring管理的Job

@Component
public class SpringJob implements Job {
    
    @Autowired
    private SomeService someService;  // 可以注入Spring Bean
    
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        someService.doSomething();
    }
}

八、监听器

1. Job监听器

public class MyJobListener implements JobListener {
    
    @Override
    public String getName() {
        return "MyJobListener";
    }
    
    @Override
    public void jobToBeExecuted(JobExecutionContext context) {
        System.out.println("Job即将执行: " + context.getJobDetail().getKey());
    }
    
    @Override
    public void jobExecutionVetoed(JobExecutionContext context) {
        System.out.println("Job执行被否决: " + context.getJobDetail().getKey());
    }
    
    @Override
    public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
        System.out.println("Job执行完成: " + context.getJobDetail().getKey());
    }
}

2. 注册监听器

scheduler.getListenerManager().addJobListener(new MyJobListener());
// 优雅关闭
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    try {
        scheduler.shutdown(true);
    } catch (SchedulerException e) {
        e.printStackTrace();
    }
}));

九、总结

  • 任务幂等性:确保任务可以重复执行而不会产生副作用。

  • 异常处理:合理处理任务执行中的异常。

  • 集群部署:在生产环境中使用数据库持久化支持集群。

  • 监控日志:记录任务执行日志,便于排查问题。

  • 资源清理:应用关闭时正确关闭调度器。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐