Java定时任务-02-Quartz


系列文章:
Jaca定时任务-01-进程级别的Timer,ScheduledExecutorService,springtask

一:Quartz定时任务框架

1:简介

Quartz是一个定时任务框架

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能:

持久性作业 - 就是保持调度定时的状态;
作业管理 - 对调度作业进行有效的管理

2:特点:作业调度

拿火车票购票来说,当你下单后,后台就会插入一条待支付的task(job),一般是30分钟,超过30min后就会执行这个job,去判断你是否支付,未支付就会取消此次订单;当你支付完成之后,后台拿到支付回调后就会再插入一条待消费的task(job),Job触发日期为火车票上的出发日期,超过这个时间就会执行这个job,判断是否使用等。

在我们实际的项目中,当Job过多的时候,肯定不能人工去操作,这时候就需要一个任务调度框架,帮我们自动去执行这些程序。那么该如何实现这个功能呢?

我们可以给作业安排在一个给定的触发时运行,触发器可以设置下列:

在一天的某个时间

在一周的某天
在一年的某个时间

3:作业持久层

可以将定时任务存在表里,这样我们就可以定时修改,也可以动态修改定时任务,也不会像timer一样一旦重启,线程池中的定时任务就没有了

二:Quartz的使用

1:导入依赖

为了演示方便,我们创建一个maven的web项目,首先导入依赖

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

2:创建自定义任务类,实现Job接口

package com.job.quartz;

import org.quartz.*;

import java.util.Date;

/**
 * @author wkl
 * @create 2022-07-04 15:51
 */
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        //获取context上下文中的JobDetail对象
        JobDetail jobDetail = jobExecutionContext.getJobDetail();
        //获取JobDetail对象中提前放进去的name属性的值-jobdataMap可以放置多个
        String name = jobDetail.getJobDataMap().getString("name");
        System.out.println(name);
        System.out.println("在这里我可以执行定时任务,比如发短信:"+new Date());
    }
}

3:quartz调度示例

package com.job.quartz;

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

/**
 * @author wkl
 * @create 2022-07-04 16:11
 */
public class TestScheduler {
    public static void main(String[] args) {
        try {
            //1.定义JobDetail,将HelloJob类添加到JobDetail对象中(添加到Job清单)
            JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                    .withDescription("Job的描述")
                    //定义JobDetail的JobDetail 'group1.job1':  jobClass: 'com.xr.quartz.HelloJob
                    .withIdentity("helloJob", "jobGroup1")
                    //定义JobDetail中的属性(可选),这些属性可以在job任务类中的jobDataMap中获取
                    .usingJobData("name", "admin")
                    .usingJobData("age", 18)
                    .build();

            //2.定义Trigger触发器,使用简单触发器,设置name/group
            //Date startTime = new Date(System.currentTimeMillis() + 3*1000L); 3秒后启动任务
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withDescription("触发器的描述")
                    //触发器名,组名(与任务组group1不是一个)
                    .withIdentity("trigger1", "TriggerGroup1")
                    //立即启动(默认),如设为某一时间再启动使用:.startAt(statTime)
                    .startNow()
                    //.withSchedule(SimpleScheduleBuilder.simpleSchedule() //创建简单触发器,使用SimpleTrigger
                    //.withIntervalInSeconds(5) //每隔5秒执行
                    //.withRepeatCount(2))      //执行2次(加上第一次执行的会显示3次)
                    //.repeatForever())         //一直执行,奔腾到老不停歇
                    //.build();
                    .withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?"))
                    .build();

            //3.创建scheduler调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            scheduler.scheduleJob(jobDetail, trigger);
            //启动调度器
            scheduler.start();

//            //运行一段时间后关闭
//            Thread.sleep(20000);
//
//            //4.关闭任务调度
//            scheduler.shutdown(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4:@DisallowConcurrentExecution和@PersistJobDataAfterExecution

  • @DisallowConcurrentExecution:禁止并发地执行同一个job定义(JobDetail定义的group)的多个实例
    主要表现为当调度任务2s执行一次,但是执行这个job就花费了3s,如果不加这个注解,那么系统会多开一个线程来执行,保证2s一次,如果加了这个注解,那么会优先执行完上一个job后,再执行下一个
  • @PersistJobDataAfterExecution:持久化obDetailr中的obDataMap(对trigger中的datamap无效),因为sheduler每次执行,都会根据JobDetail创建一个新的Job实例,索引从context中获取的jobDateMap都是属于每一个实例的,加上这个注解,获取的jobDateMap中的参数就是一致的,这样可以共同使用参数,比如count计数

5:quartz的核心类说明

分别是 Scheduler(调度器)Job(任务)和 Trigger (触发器),它们是我们使用 Quartz 的关键。

  • (1)、Job:定义需要执行的任务。该类需要实现Job接口,只定义一个方法 execute(JobExecutionContext context),在实现类的 execute 方法中编写所需要定时执行的 Job(任务), JobExecutionContext 类提供了调度应用的一些信息。Job 运行时的信息保存在 JobDataMap 实例中。
  • (2)、Trigger:负责设置调度策略。该类是一个接口,描述触发 job 执行的时间触发规则。主要有 SimpleTrigger 和 CronTrigger 这两个子类。当且仅当需调度一次或者以固定时间间隔周期执行调度,SimpleTrigger 是最适合的选择;而 CronTrigger 则可以通过 Cron 表达式定义出各种复杂时间规则的调度方案:如工作日的10:00点执行异常考勤邮件通知
  • (3)、Scheduler:调度器就相当于一个容器,装载着任务和触发器。该类是一个接口,代表一个 Quartz 的独立运行容器, Trigger 和 JobDetail 可以注册到 Scheduler 中, 两者在 Scheduler 中拥有各自的组及名称, 组及名称是 Scheduler 查找定位容器中某一对象的依据, Trigger 的组及名称必须唯一, JobDetail 的组和名称也必须唯一(但可以和 Trigger 的组和名称相同,因为它们是不同类型的)。Scheduler 定义了多个接口方法, 允许外部通过组及名称访问和控制容器中 Trigger 和 JobDetail。
  • (4)、JobDetail:描述 Job 的实现类及其它相关的静态信息,如:Job 名字、描述、关联监听器等信息。Quartz 每次调度 Job 时, 都重新创建一个 Job 实例, 所以它不直接接受一个 Job 的实例,相反它接收一个 Job 实现类,以便运行时通过 newInstance() 的反射机制实例化 Job。
  • (5)、一个任务可以被多个触发器调度,但是一个触发器只能调度一个任务;
    在这里插入图片描述

三:springboot集成Quartz-Quartz持久化-利用springboot数据源

之前我们对quartz的基本使用进行了演示,但是这些job的相关信息都是存储在内存中,对于一个持续定时任务比如执行100次一段逻辑,我们有时希望重新启动程序后应该将剩下的次数执行完,而不是重新执行100次。这时就需要持久化。
当然还有另一个原因,job持久化就意味着可以web管理job!

1:数据库准备

Quartz 存储任务信息有两种方式,使用内存或者使用数据库来存储,这里我们采用 MySQL 数据库存储的方式,首先需要新建 Quartz 的相关表,sql 脚本下载地址:http://www.quartz-scheduler.org/downloads/,也可以从我们下载的jar包的org.quartz.impl.jdbcjobstore下可以看到有很多的sql ,找到名称为 tables_mysql.sql,
在这里插入图片描述

创建成功后数据库中多出 11 张表
在这里插入图片描述
表代表的意思如下:
在这里插入图片描述
各个表字段含义可参考:https://blog.csdn.net/sqlgao22/article/details/100697214

2:导入依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--quartz-->
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-quartz -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
            <version>2.7.0</version>
        </dependency>

        <!--mysql-->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>

Quartz 默认使用 c3p0作为数据库连接池

3:配置文件

默认情况下,Quartz 会加载 classpath 下的 quartz.properties 作为配置文件。如果找不到,则会使用 quartz 框架自己 jar 包下 org/quartz 底下的 quartz.properties 文件
application.yml:

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/quartz?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
  quartz:
    job-store-type: jdbc
    jdbc:
#      后续运行就可以改成never-表示不再执行sql重新建表语句
      initialize-schema: always
    properties:
      org:
        quartz:
          threadPool:
            threadCount: 5  #更改quartz线程池

4:配置quartz工具类

当springboot集成quartz后,可以直接在组件中注入scheduler,默认使用数据库数据源,其他配置都会读取配置文件

package com.example.boot_quartz.utils;

import com.example.boot_quartz.dto.QuartzBean;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Objects;

/**
 * @BelongsProject: boot_quartz
 * @BelongsPackage: com.example.boot_quartz.utils
 * @Author: wangkanglu
 * @CreateTime: 2022-07-06  17:25
 * @Description: TODO
 * @Version: 1.0
 */
@Component
public class QuartzUtils {

    @Autowired
    private Scheduler scheduler;


    /**
     * 创建定时任务 定时任务创建之后默认启动状态
     *
     * @param quartzBean 定时任务信息类
     * @throws Exception
     */
    public void createScheduleJob(QuartzBean quartzBean, Map<String, String> map) {
        try {
            //获取到定时任务的执行类  必须是类的绝对路径名称
            //定时任务类需要是job类的具体实现 QuartzJobBean是job的抽象类。
            Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(quartzBean.getJobClass());
            // 构建定时任务信息
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(quartzBean.getJobName()).build();
            JobDataMap jobDataMap = jobDetail.getJobDataMap();
            if(!Objects.isNull(map) && !map.isEmpty()){
                map.forEach((key, value) -> jobDataMap.put(key, value));

            }
            // 设置定时任务执行方式
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression());
            // 构建触发器trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartzBean.getJobName()).withSchedule(scheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (ClassNotFoundException e) {
            System.out.println("定时任务类路径出错:请输入类的绝对路径");
        } catch (SchedulerException e) {
            System.out.println("创建定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 根据任务名称暂停定时任务
     *
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public void pauseScheduleJob(String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            System.out.println("暂停定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 根据任务名称恢复定时任务
     *
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public void resumeScheduleJob(String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
            System.out.println("启动定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 根据任务名称立即运行一次定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public void runOnce(Scheduler scheduler, String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            System.out.println("运行定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 更新定时任务
     *
     * @param quartzBean 定时任务信息类
     * @throws SchedulerException
     */
    public void updateScheduleJob(QuartzBean quartzBean) {
        try {
            //获取到对应任务的触发器
            TriggerKey triggerKey = TriggerKey.triggerKey(quartzBean.getJobName());
            //设置定时任务执行方式
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression());
            //重新构建任务的触发器trigger
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            //重置对应的job
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {
            System.out.println("更新定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 根据定时任务名称从调度器当中删除定时任务
     *
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public void deleteScheduleJob(String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
            scheduler.deleteJob(jobKey);
        } catch (SchedulerException e) {
            System.out.println("删除定时任务出错:" + e.getMessage());
        }
    }

}

5:配置定时任务类

package com.example.boot_quartz.job;

import com.example.boot_quartz.utils.DateUtils;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.StringJoiner;

/**
 * @BelongsProject: boot_quartz
 * @BelongsPackage: com.example.boot_quartz.job
 * @Author: wangkanglu
 * @CreateTime: 2022-07-06  15:42
 * @Description: TODO 这是spring框架下的job,他集成了spring封装后的job,在这里可以直接使用@autoworid注入其他组件,然后执行方法
 * @Version: 1.0
 */
public class SpringJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        StringJoiner stringJoiner = new StringJoiner("---");
        stringJoiner
                .add("定时任务执行了")
                .add(DateUtils.getCurrenDateOfString())
                .add(Thread.currentThread().getName())
                .add(context.getTrigger().getKey().getName());

        System.out.println(stringJoiner);
    }
}

6:配置任务实体类

package com.example.boot_quartz.dto;

import lombok.Data;

/**
 * @BelongsProject: boot_quartz
 * @BelongsPackage: com.example.boot_quartz.dto
 * @Author: wangkanglu
 * @CreateTime: 2022-07-06  17:26
 * @Description: TODO
 * @Version: 1.0
 */
@Data
public class QuartzBean {
    /** 任务id */
    private String  id;

    /** 任务名称 */
    private String jobName;

    /** 任务执行类 */
    private String jobClass;

    /** 任务状态 启动还是暂停*/
    private Integer status;

    /** 任务运行时间表达式 */
    private String cronExpression;
}

7:配置controller实现web控制定时任务

package com.example.boot_quartz.controller;

import com.example.boot_quartz.dto.QuartzBean;
import com.example.boot_quartz.utils.QuartzUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @BelongsProject: boot_quartz
 * @BelongsPackage: com.example.boot_quartz.controller
 * @Author: wangkanglu
 * @CreateTime: 2022-07-06  17:36
 * @Description: TODO
 * @Version: 1.0
 */
@RestController
public class QuartzController {
    @Autowired
    QuartzUtils quartzUtils;


    @GetMapping("/addjob")
    public String addJob(){
        try {
            //这些到时候从前台传入参数,实现web控制
            QuartzBean quartzBean = new QuartzBean();
            quartzBean.setJobClass("com.example.boot_quartz.job.SpringJob");
            quartzBean.setJobName("job");
            quartzBean.setCronExpression("0/4 * * * * ?");

            quartzUtils.createScheduleJob(quartzBean,null);
            return "成功!";
        } catch (Exception e) {
            e.printStackTrace();
            return "失败!";
        }

    }
}

四:springboot集成Quartz-Quartz持久化-使用quartz自己的数据源

之前我们对quartz的基本使用进行了演示,但是这些job的相关信息都是存储在内存中,对于一个持续定时任务比如执行100次一段逻辑,我们有时希望重新启动程序后应该将剩下的次数执行完,而不是重新执行100次。这时就需要持久化。
当然还有另一个原因,job持久化就意味着可以web管理job!

1:数据库准备

Quartz 存储任务信息有两种方式,使用内存或者使用数据库来存储,这里我们采用 MySQL 数据库存储的方式,首先需要新建 Quartz 的相关表,sql 脚本下载地址:http://www.quartz-scheduler.org/downloads/,也可以从我们下载的jar包的org.quartz.impl.jdbcjobstore下可以看到有很多的sql ,找到名称为 tables_mysql.sql,
在这里插入图片描述

创建成功后数据库中多出 11 张表
在这里插入图片描述
表代表的意思如下:
在这里插入图片描述
各个表字段含义可参考:https://blog.csdn.net/sqlgao22/article/details/100697214

2:导入依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--quartz-->
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-quartz -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
            <version>2.7.0</version>
        </dependency>

        <!--mysql-->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>

Quartz 默认使用 c3p0作为数据库连接池

3:配置文件和对应数据源@QuartzDataSource

默认情况下,Quartz 会加载 classpath 下的 quartz.properties 作为配置文件。如果找不到,则会使用 quartz 框架自己 jar 包下 org/quartz 底下的 quartz.properties 文件
application.yml:

server:
  port: 8080

spring:
  datasource:
    db1:
      url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
      username: root
      password: 123456
      driver-class-name: com.mysql.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource
    quartz:
      url: jdbc:mysql://localhost:3306/quartz?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
      username: root
      password: 123456
      driver-class-name: com.mysql.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource

  quartz:
    job-store-type: jdbc
    jdbc:
      initialize-schema: always
#    properties:
#      org:
#        quartz:
#          scheduler:
#            instanceName: DemoScheduler
#            instanceId: AUTO
#          jobStore:
#            class: org.quartz.impl.jdbcjobstore.JobStoreTX
#            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate #跟数据库初始化脚本中配置保持一致
#            tablePrefix: QRTZ_
#            isClustered: true
#            clusterCheckinInterval: 10000
#            useProperties: false
#          threadPool:
#            class: org.quartz.simpl.SimpleThreadPool
#            threadCount: 20
#            threadPriority: 5
#            threadsInheritContextClassLoaderOfInitializingThread: true



package com.example.boot_quartz.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.sun.istack.internal.NotNull;
import org.quartz.Scheduler;
import org.quartz.ee.servlet.QuartzInitializerListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.boot.autoconfigure.quartz.QuartzDataSource;
import org.springframework.boot.autoconfigure.quartz.SchedulerFactoryBeanCustomizer;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;

/**
 * @BelongsProject: boot_quartz
 * @BelongsPackage: com.example.boot_quartz.config
 * @Author: wangkanglu
 * @CreateTime: 2022-07-06  18:06
 * @Description: TODO
 * @Version: 1.0
 */
@Configuration
public class QuartzConfig {


    @Primary
    @Bean(name = "dataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    public DruidDataSource dataSource2 () {
        return DruidDataSourceBuilder.create().build();
    }

    //使用这个注解,表明这是quartz的数据源
    @Bean(name = "dataSourceQuartz")
    @ConfigurationProperties(prefix = "spring.datasource.quartz")
    @QuartzDataSource
    public DruidDataSource dataSourceTask () {
        return DruidDataSourceBuilder.create().build();
    }
}

4:配置quartz工具类

当springboot集成quartz后,可以直接在组件中注入scheduler,默认使用数据库数据源,其他配置都会读取配置文件

package com.example.boot_quartz.utils;

import com.example.boot_quartz.dto.QuartzBean;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Objects;

/**
 * @BelongsProject: boot_quartz
 * @BelongsPackage: com.example.boot_quartz.utils
 * @Author: wangkanglu
 * @CreateTime: 2022-07-06  17:25
 * @Description: TODO
 * @Version: 1.0
 */
@Component
public class QuartzUtils {

    @Autowired
    private Scheduler scheduler;


    /**
     * 创建定时任务 定时任务创建之后默认启动状态
     *
     * @param quartzBean 定时任务信息类
     * @throws Exception
     */
    public void createScheduleJob(QuartzBean quartzBean, Map<String, String> map) {
        try {
            //获取到定时任务的执行类  必须是类的绝对路径名称
            //定时任务类需要是job类的具体实现 QuartzJobBean是job的抽象类。
            Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(quartzBean.getJobClass());
            // 构建定时任务信息
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(quartzBean.getJobName()).build();
            JobDataMap jobDataMap = jobDetail.getJobDataMap();
            if(!Objects.isNull(map) && !map.isEmpty()){
                map.forEach((key, value) -> jobDataMap.put(key, value));

            }
            // 设置定时任务执行方式
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression());
            // 构建触发器trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartzBean.getJobName()).withSchedule(scheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (ClassNotFoundException e) {
            System.out.println("定时任务类路径出错:请输入类的绝对路径");
        } catch (SchedulerException e) {
            System.out.println("创建定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 根据任务名称暂停定时任务
     *
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public void pauseScheduleJob(String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            System.out.println("暂停定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 根据任务名称恢复定时任务
     *
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public void resumeScheduleJob(String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
            System.out.println("启动定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 根据任务名称立即运行一次定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public void runOnce(Scheduler scheduler, String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            System.out.println("运行定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 更新定时任务
     *
     * @param quartzBean 定时任务信息类
     * @throws SchedulerException
     */
    public void updateScheduleJob(QuartzBean quartzBean) {
        try {
            //获取到对应任务的触发器
            TriggerKey triggerKey = TriggerKey.triggerKey(quartzBean.getJobName());
            //设置定时任务执行方式
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression());
            //重新构建任务的触发器trigger
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            //重置对应的job
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {
            System.out.println("更新定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 根据定时任务名称从调度器当中删除定时任务
     *
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public void deleteScheduleJob(String jobName) {
        JobKey jobKey = JobKey.jobKey(jobName);
        try {
            scheduler.deleteJob(jobKey);
        } catch (SchedulerException e) {
            System.out.println("删除定时任务出错:" + e.getMessage());
        }
    }

}

5:配置定时任务类

package com.example.boot_quartz.job;

import com.example.boot_quartz.utils.DateUtils;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.StringJoiner;

/**
 * @BelongsProject: boot_quartz
 * @BelongsPackage: com.example.boot_quartz.job
 * @Author: wangkanglu
 * @CreateTime: 2022-07-06  15:42
 * @Description: TODO 这是spring框架下的job,他集成了spring封装后的job,在这里可以直接使用@autoworid注入其他组件,然后执行方法
 * @Version: 1.0
 */
public class SpringJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        StringJoiner stringJoiner = new StringJoiner("---");
        stringJoiner
                .add("定时任务执行了")
                .add(DateUtils.getCurrenDateOfString())
                .add(Thread.currentThread().getName())
                .add(context.getTrigger().getKey().getName());

        System.out.println(stringJoiner);
    }
}

6:配置任务实体类

package com.example.boot_quartz.dto;

import lombok.Data;

/**
 * @BelongsProject: boot_quartz
 * @BelongsPackage: com.example.boot_quartz.dto
 * @Author: wangkanglu
 * @CreateTime: 2022-07-06  17:26
 * @Description: TODO
 * @Version: 1.0
 */
@Data
public class QuartzBean {
    /** 任务id */
    private String  id;

    /** 任务名称 */
    private String jobName;

    /** 任务执行类 */
    private String jobClass;

    /** 任务状态 启动还是暂停*/
    private Integer status;

    /** 任务运行时间表达式 */
    private String cronExpression;
}

7:配置controller实现web控制定时任务

package com.example.boot_quartz.controller;

import com.example.boot_quartz.dto.QuartzBean;
import com.example.boot_quartz.utils.QuartzUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @BelongsProject: boot_quartz
 * @BelongsPackage: com.example.boot_quartz.controller
 * @Author: wangkanglu
 * @CreateTime: 2022-07-06  17:36
 * @Description: TODO
 * @Version: 1.0
 */
@RestController
public class QuartzController {
    @Autowired
    QuartzUtils quartzUtils;


    @GetMapping("/addjob")
    public String addJob(){
        try {
            //这些到时候从前台传入参数,实现web控制
            QuartzBean quartzBean = new QuartzBean();
            quartzBean.setJobClass("com.example.boot_quartz.job.SpringJob");
            quartzBean.setJobName("job");
            quartzBean.setCronExpression("0/4 * * * * ?");

            quartzUtils.createScheduleJob(quartzBean,null);
            return "成功!";
        } catch (Exception e) {
            e.printStackTrace();
            return "失败!";
        }

    }
}

四:Cron表达式

1:CronTrigger配置格式:

格式: [秒] [分] [小时] [日] [月] [周] [年]
在这里插入图片描述

2:通配符说明:

  • [* ] 表示所有值. 例如:在分的字段上设置 “*”,表示每一分钟都会触发。
  • ? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为"?" 具体设置为 0 0 0 10 * ?
    • 表示区间。例如 在小时上设置 “10-12”,表示 10,11,12点都会触发。
  • , 表示指定多个值,例如在周字段上设置 “MON,WED,FRI” 表示周一,周三和周五触发
  • / 用于递增触发。如在秒上面设置"5/15" 表示从5秒开始,每增15秒触发(5,20,35,50)。在月字段上设置’1/3’所示每月1号开始,每隔三天触发一次。
  • L 表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年[leap]), 在周字段上表示星期六,相当于"7"或"SAT"。如果在"L"前加上数字,则表示该数据的最后一个。例如在周字段上设置"6L"这样的格式,则表示“本月最后一个星期五"
  • W 表示离指定日期的最近那个工作日(周一至周五). 例如在日字段上设置"15W",表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 “1W”,它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,“W"前只能设置具体的数字,不允许区间”-").
  • 【#】序号(表示每月的第几个周几),例如在周字段上设置"6#3"表示在每月的第三个周六.注意如果指定"#5",正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了)
    tips

3:举例说明

表达式说明
0 0 12 * * ?每天12点触发
0 15 10 ? * *每天10点15分触发
0 15 10 * * ?每天10点15分触发
0 15 10 * * ? *每天10点15分触发
0 15 10 * * ?2005 2005年每天10点15分触发
0 * 14 * * ?每天下午的 2点到2点59分每分触发
0 0/5 14 * * ?每天下午的 2点到2点59分(整点开始,每隔5分触发)
0 0/5 14,18 * * ?每天下午的 2点到2点59分(整点开始,每隔5分触发)每天下午的 18点到18点59分(整点开始,每隔5分触发)
0 0-5 14 * * ?每天下午的 2点到2点05分每分触发
0 10,44 14 ? 3 WED3月分每周三下午的 2点10分和2点44分触发
0 15 10 ? * MON-FRI从周一到周五每天上午的10点15分触发
0 15 10 15 * ?每月15号上午10点15分触发
0 15 10 L * ?每月最后一天的10点15分触发
0 15 10 ? * 6L每月最后一周的星期五的10点15分触发
0 15 10 ? * 6L 2002-2005从2002年到2005年每月最后一周的星期五的10点15分触发
0 15 10 ? * 6#3每月的第三周的星期五开始触发
0 0 12 1/5 * ?每月的第一个中午开始每隔5天触发一次
0 11 11 11 11 ?每年的11月11号 11点11分触发(光棍节)

4:在线cron表达式生成: http://qqe2.com/cron/inde

文章参考:https://blog.csdn.net/little__SuperMan/article/details/82722104

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java Quartz是一个开源的作业调度框架,可以用来创建简单或复杂的定时任务。它支持各种不同的触发器类型,包括简单的时间间隔触发器、Cron表达式触发器、日历触发器和可编程触发器。Java Quartz还提供了一些特性来管理和监控作业调度,例如集群功能、持久化、作业执行历史记录、错误处理和作业调度统计信息等。 以下是一个简单的Java Quartz定时任务的示例代码: ```java import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; public class QuartzJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { // 定时任务执行的代码 System.out.println("Hello, Quartz!"); } public static void main(String[] args) throws SchedulerException { // 创建一个定时任务调度器 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 创建一个定时任务触发器 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(5) .repeatForever()) .build(); // 创建一个定时任务 JobDetail job = JobBuilder.newJob(QuartzJob.class) .withIdentity("job1", "group1") .build(); // 将定时任务和触发器绑定到调度器上 scheduler.scheduleJob(job, trigger); // 启动定时任务调度器 scheduler.start(); } } ``` 这个代码会创建一个定时任务,每隔5秒钟输出一次“Hello, Quartz!”。可以根据需要修改定时任务触发器的设置,例如修改触发器的时间间隔、起始时间、结束时间等。可以使用Java Quartz的其他功能来实现更复杂的定时任务逻辑

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苍煜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值