锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

Jaca定时任务-02-Quartz

时间:2022-11-14 12:30:01 温度传感器wkl

文章目录

  • 一:Quartz定时任务框架
    • 1:简介
    • 特点:作业调度
    • 3:作业持久层
  • 二:Quartz的使用
    • 1:导入依赖
    • 2.创建自定义任务类,实现Job接口
    • 3:quartz调度示例
    • 4:@DisallowConcurrentExecution和@PersistJobDataAfterExecution
    • 5:quartz核心类说明
  • 三:springboot集成Quartz-Quartz持久化-利用springboot数据源
    • 1.数据库准备
    • 2:导入依赖
    • 3:配置文件
    • 4:配置quartz工具类
    • 五、配置定时任务类
    • 六、配置任务实体类
    • 7:配置controller实现web控制定时任务
  • 四:springboot集成Quartz-Quartz持久化-使用quartz自己的数据源
    • 1.数据库准备
    • 2:导入依赖
    • 3:配置文件和对应数据源@QuartzDataSource
    • 4:配置quartz工具类
    • 5:配置定时任务类
    • 六、配置任务实体类
    • 7:配置controller实现web控制定时任务
  • 四:Cron表达式
    • 1:CronTrigger配置格式:
    • 2:通配符说明:
    • 3:举例说明
    • 4:在线cron表达式生成: [http://qqe2.com/cron/inde](http://qqe2.com/cron/inde)

一:Quartz定时任务框架

1:简介

Quartz是定时任务框架

Quartz是OpenSymphony开源组织在Job scheduling该领域的另一个开源项目完全由Java开发类似于执行定期任务java.util.Timer。但是相较于Timer, Quartz增加了许多功能:

持久性作业 - 保持定期调度;
作业管理 - 有效管理调度作业

特点:作业调度

以火车票购票为例。下单后,后台会插入一张待付款的票。task(job),一般30分钟以上,30分钟以上min这将在之后执行job,判断你是否付款,如果你不付款,订单将被取消;付款完成后,后台会插入一个待消费的回调task(job),Job触发日期是火车票上的出发日期,超过这个时间就会执行这个job,判断是否使用等。

在我们的实际项目中,当Job太多时候,我们不能手动操作。此时,我们需要一个任务调度框架来帮助我们自动执行这些程序。那么如何实现这个功能呢?

我们可以在给定触发时安排操作,触发器可以设置如下:

一天的某个时间

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

三、作业持久层

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

二:Quartz的使用

1:导入依赖

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

 <dependency>       <groupId>org.quartz-schedulergroupId>       <artifactId>quartzartifactId>       <version>2.3.2version>   dependency>   <dependency>       <groupId>org.quartz-schedulergroupId>       <artifactId>quartz-jobsartifactId>       <version>2.3.2version>
 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.bootgroupId>
            <artifactId>spring-boot-starterartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-quartzartifactId>
            <version>2.7.0version>
        dependency>

        
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.47version>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-jdbcartifactId>
        dependency>

        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.24version>
            <scope>providedscope>
        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("定时任务执行了")
                . 

相关文章