一、quartz介绍

1.quartz是一个任务调度框架。

2.优点:

(1)支持多种类型的触发器(如简单触发器、cron触发器等)。

(2)支持作业和触发器的持久化(保存到数据库中)。

(3)支持分布式和集群模式。

3.核心概念:

(1)Trigger(触发器)

设置Job调度的时间触发规则。 

(2)Job(任务)和JobDetail

Job:实现Job接口,在execute方法中编写具体业务逻辑。

JobDetail:用来存储Job实例的信息,调度器需要借助JobDetail对象添加Job实例。

(3)Scheduler(调度器)

任务调度容器,Trigger和Job需要加入到Scheduler中才能使用。

二、quartz应用

1、pom文件引用依赖

<!--quart任务调度依赖-->
<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

2、建表(持久化准备)

在这个地方可以找到sql文件。

java到时提醒功能 定时提醒java_java到时提醒功能

3、添加配置文件

spring:
  quartz:
    job-store-type: jdbc  #将任务保存到数据库
    wait-for-jobs-to-complete-on-shutdown: true #程序结束时会等待quartz相关内容结束
    overwrite-existing-jobs: true #启动时更新已存在的job
    jdbc:
      initialize-schema: never  #不初始化数据库
    properties:
      org:
        quartz:
          scheduler:
            instanceName: scheduler #实例名
            instanceId: AUTO #实例编号自动生成
            jobStore:
              class: org.quartz.impl.jdbcjobstore.JobStoreTX #数据保存方式为数据库持久化
              driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate #数据库代理类
              tablePrefix: QRTZ_ #数据表的前缀
              isClustered: false #是否以集群方式运行
              clusterCheckinInterval: 10000 #调度实例失效的检查时间间隔,单位毫秒
              useProperties: true #JobDataMaps是否都为String类型
              misfireThreshold: 60000 #最大能忍受的触发超时时间
            threadPool:
              class: org.quartz.simpl.SimpleThreadPool #连接池实现类
              threadCount: 10 #线程数量
              threadPriority: 5 #线程优先级
              threadsInheritContextClassLoaderOfInitializingThread: true #配置是否启动自动加载数据库内的定时任务,默认true

4、创建定时任务配置类

需要手动注入Bean。

package com.feng.config;

import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

/**
 * 定时任务配置类
 *
 * @Author: feng
 */
@Configuration
public class ScheduleConfig {

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    @Bean(name = "scheduler")
    public Scheduler scheduler() {
        return schedulerFactoryBean.getScheduler();
    }
}

5、创建实体类

package com.feng.pojo;

import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.quartz.JobDataMap;

/**
 * quartz任务请求参数
 *
 * @Author: feng
 * @DateTime: 2023/11/16 20:31
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class QuartzParam {

    @ApiModelProperty(value = "任务路径")
    private String jobClassName;

    @ApiModelProperty(value = "任务名称")
    private String jobName;

    @ApiModelProperty(value = "任务组名")
    private String jobGroup;

    @ApiModelProperty(value = "任务数据")
    private JobDataMap jobDataMap;

    @ApiModelProperty(value = "cron表达式")
    private String cron;

    @ApiModelProperty(value = "任务描述")
    private String description;

}

6、创建Controller(为了方便测试,手动添加了一些数据)

package com.feng.controller;

import com.feng.pojo.QuartzParam;
import com.feng.service.QuartzService;
import lombok.RequiredArgsConstructor;
import org.quartz.JobDataMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.UUID;

/**
 * quartz控制类
 *
 * @Author: feng
 * @DateTime: 2023/11/16 20:22
 */

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

    @Autowired
    private QuartzService quartzService;

    @RequestMapping("/addJob")
    public void addJob() {

        QuartzParam param = new QuartzParam();

        //测试数据
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("meetingId", UUID.randomUUID().toString().replace("-", ""));
        jobDataMap.put("meetingName", "会议1");
        jobDataMap.put("startTime", new Date());
        jobDataMap.put("meetingPeople", "小明,小王,小红");

        param.setJobName("meeting1");
        param.setJobGroup("meetingMsg");
        param.setJobDataMap(jobDataMap);
        param.setJobClassName("com.feng.controller.Meeting.addMeeting()");
        param.setCron("1 * * * * ?");   //1分钟执行一次

        quartzService.addJob(param);
    }
}

7、创建任务调度

package com.feng.service.impl;

import com.feng.job.QuartzJob;
import com.feng.pojo.QuartzParam;
import com.feng.service.QuartzService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @Author: feng
 * @DateTime: 2023/11/16 20:40
 */
@Service
@Slf4j
public class QuartzServiceImpl implements QuartzService {

    @Resource(name = "scheduler")
    private Scheduler scheduler;

    @Override
    public void addJob(QuartzParam param) {

        String jobName = param.getJobName();
        String jobGroup = param.getJobGroup();
        String cron = param.getCron();
        String description = param.getDescription();
        JobDataMap jobDataMap = param.getJobDataMap();

        JobKey jobKey = new JobKey(jobName, jobGroup);

        try {

            if (scheduler.checkExists(jobKey)) {
                log.error("该任务名称及任务组已存在!");
            }

            //创建作业调度JobDetail
            JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class)
                    .withIdentity(jobKey)   //由jobName和jobGroup组成的唯一标识
                    .withDescription(description)   //说明
                    .usingJobData(jobDataMap)       //用到的参数
                    .build();

            //通过触发器名和cron 表达式创建 Trigger
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity("meetingTrigger")       //触发器名称
                    .startNow()     //指定开始时间
                    .withSchedule(CronScheduleBuilder.cronSchedule(cron))    //cron表达式
                    .build();

            //开启定时器
            scheduler.scheduleJob(jobDetail, trigger);
            scheduler.start();

            log.info("添加成功!- " + jobKey);

        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
    }
}

8、编写任务的具体业务逻辑

package com.feng.job;

import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 定时器触发的任务
 *
 * @Author: feng
 * @DateTime: 2023/11/21 19:18
 */
@Slf4j
public class QuartzJob implements Job {
    
    @Override
    public void execute(JobExecutionContext jobExecutionContext) {

        log.info("执行QuartzJob中的逻辑" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));

        log.info("会议信息:" + jobExecutionContext.getJobDetail().getJobDataMap().toString());

        String meetingId = jobExecutionContext.getJobDetail().getJobDataMap().get("meetingId").toString();

        //TODO
        //会议通知消息发送逻辑

        log.info("定时任务发送了会议ID为:" + meetingId + "的消息通知");

    }
}

启动项目后,出现以下文字,说明已开启持久化了

java到时提醒功能 定时提醒java_定时任务_02

执行方法后数据库中就有相关的记录,重启项目也不会丢失啦

java到时提醒功能 定时提醒java_java_03

9、删除定时任务

先调用schedulercheckExists()方法,根据jobKey判断数据库中是否存在该定时任务,存在的话就调用deleteJob()方法删除指定定时任务。

三、总结

以上是quartz的一些使用方法,写了一个会议通知推送的简单例子,思路大概是这样。简单记录一下学习过程。