Quartz入门

Quartz任务调度

1. Quartz概念

  • Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目
  • Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。
  • Quartz 可以与 J2EE 与 J2SE 应用程序相结合也可以单独使用。
  • Quartz 允许程序开发人员根据时间的间隔来调度作业。
  • Quartz 实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。

2. 核心

  • Job 表示一个工作,要执行的具体内容。此接口中只有一个方法

    • void execute(JobExecutionContext context) throws JobExecutionException;
      
  • JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。

  • Trigger 触发器,代表一个调度参数的配置,什么时候去调。

  • Scheduler 调度器,代表一个调度容器,一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。

3. 运行环境

  • Quartz 可以运行嵌入在另一个独立式应用程序。
  • Quartz 可以在应用程序服务器(或 servlet 容器)内被实例化,并且参与 XA 事务。
  • Quartz 可以作为一个独立的程序运行(其自己的 Java 虚拟机内),可以通过 RMI 使用。
  • Quartz 可以被实例化,作为独立的项目集群(负载平衡和故障转移功能),用于作业的执行。

4. 常用API

  • Scheduler 用于与调度程序交互的主程序接口
    • 调度程序-任务执行计划表,只有安排进执行计划的任务Job(通过Schedule.scheduleJob方法安排进任务计划),任务触发trigger,任务才会执行
  • Job 任务类,如果自定义任务的话,需要实现Job接口,实现void execute(JobExecutionContext context) throws JobExecutionException;方法
  • JobDetail 定义任务Job的实例,通过JobBuilder类创建
  • JobDataMap Map接口实现类,存储Job实例执行过程中的数据
  • Trigger触发器,用来执行Job任务
  • JobBuilder 用于声明一个任务实例,可定义该任务的任务名,组名等
  • TriggerBuilder 触发器创建类,用于创建触发器trigger的实例
  • JobListener、TriggerListener、SchedulerListener监听器,用于对组件的监听

5. Quartz的使用

5.1 依赖引入

  • <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
    <dependency>
      <groupId>org.quartz-scheduler</groupId>
      <artifactId>quartz</artifactId>
      <version>2.3.0</version>
    </dependency>
    
  • 创建Job接口实现类,实现Job接口,实现execute方法

  • /**
     * @Author: Hjx
     * @Date: 2021/8/27 17:39
     */
    public class HelloJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            String string = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            System.out.println("数据库备份时间:"+string);
        }
    }
    
  • 测试Main函数类

  • /**
     * @Author: Hjx
     * @Date: 2021/8/27 17:05
     */
    public class TestQuartz {
        public static void main(String[] args) throws SchedulerException {
            // 1. 创建调度器Scheduler,从工厂中获取默认调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    
            // 2. 创建JobDetail实例
            // HelloJob.class 实现Job接口的实例类
            // "JobDetailName" JobDetail的名称,用来标识任务
            // "JobDetailGroup" 任务组名称,可省略,默认为DEFAULT
            // build() 创建JobDetail实例
            JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                    .withIdentity("JobDetailName", "JobDetailGroup")
                    .build();
    
            // 3. 触发器 Trigger
            // "TriggerName" 触发器名称 用来标识触发器
            // "TriggerGroup" 触发器组名称,可省略,默认为DEFAULT
            // startNow() 开始时间 立刻开始
            // withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)) 每5秒执行一次
            // 创建触发器 Trigger实例
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity("TriggerName", "TriggerGroup")
                    .startNow()
                    .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
                    .build();
    
            // 将任务与触发器相关联
            scheduler.scheduleJob(jobDetail, trigger);
    
            // 启动调度器
            scheduler.start();
        }
    }
    
  • 运行结果

  • 数据库备份时间:2021-08-27 18:00:45
    数据库备份时间:2021-08-27 18:00:50
    数据库备份时间:2021-08-27 18:00:55
    数据库备份时间:2021-08-27 18:01:00
    数据库备份时间:2021-08-27 18:01:05
    ······
    

6. Job和JobDetail

6.1 Job

  • Job:工作任务调度的接口,任务类需要实现该接口,该接口中的execute方法,类似JDK提供的TimeTask中的run方法,在里面编写任务执行的业务逻辑代码

6.2 Job生命周期

  • Job实例在Quartz中的生命周期:每次调度器执行Job时,它在调用execute方法钱会创建一个新的Job实例,当调用结束后,关联的Job对象就会被释放,被垃圾回收机制回收

  • 修改HelloJob类,添加无参构造方法

  • /**
     * @Author: Hjx
     * @Date: 2021/8/27 17:39
     */
    public class HelloJob implements Job {
        public HelloJob() {
            System.out.println("HelloJob任务被创建");
        }
    
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            String string = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            System.out.println("数据库备份时间:"+string);
        }
    }
    
  • 执行TestQuartz测试类,结果如下

  • HelloJob任务被创建
    数据库备份时间:2021-08-27 18:11:50
    HelloJob任务被创建
    数据库备份时间:2021-08-27 18:11:55
    HelloJob任务被创建
    数据库备份时间:2021-08-27 18:12:00
    ······
    
  • 可以看到,每执行一次任务,HelloJo类就会被创建一次

6.3 JobDetail

  • jobDetail为Job实例提供了许多设置属性,以及JobDetalMap成员变量属性,用来存储特定Job实例的状态信息,调度器需要借助JobDatail对象添加Job实例

  • 重要属性:name、group、jobClass、jobDataMap

  • 修改TestQuartz测试类

  • /**
     * @Author: Hjx
     * @Date: 2021/8/27 17:05
     */
    public class TestQuartz {
        public static void main(String[] args) throws SchedulerException {
            // 1. 创建调度器Scheduler,从工厂中获取默认调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    
            // 2. 创建JobDetail实例
            // HelloJob.class 实现Job接口的实例类
            // "JobDetailName" JobDetail的名称,用来标识任务
            // "JobDetailGroup" 任务组名称,可省略,默认为DEFAULT
            // build() 创建JobDetail实例
            JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                    .withIdentity("JobDetailName", "JobDetailGroup")
                    .build();
            
           // 获取jobDetail设置的名称
            String jobDetailName = jobDetail.getKey().getName();
            System.out.println("设置的任务名称:"+jobDetailName);
            // 获取设置的任务组名称
            String jobDetailGroup = jobDetail.getKey().getGroup();
            System.out.println("设置的任务组名称:"+jobDetailGroup);
            // 获取Job实现类的名称
            String simpleName = jobDetail.getJobClass().getSimpleName();
            System.out.println("任务实例类名称:"+simpleName);
    
            // 3. 触发器 Trigger
            // "TriggerName" 触发器名称 用来标识触发器
            // "TriggerGroup" 触发器组名称,可省略,默认为DEFAULT
            // startNow() 开始时间 立刻开始
            // withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)) 每5秒执行一次
            // 创建触发器 Trigger实例
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity("TriggerName", "TriggerGroup")
                    .startNow()
                    .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
                    .build();
    
            // 将任务与触发器相关联
            scheduler.scheduleJob(jobDetail, trigger);
    
            // 启动调度器
            scheduler.start();
        }
    }
    
  • 打印JobDetail信息

  • 设置的任务名称:JobDetailName
    设置的任务组名称:JobDetailGroup
    任务实例类名称:HelloJob
    HelloJob任务被创建
    数据库备份时间:2021-08-27 18:28:24
    HelloJob任务被创建
    数据库备份时间:2021-08-27 18:28:29
    ······
    

6.4 JobExecutionContext

  • 当Scheduler调用一个ob,就会将JobExecutionContext传递给Job的execute()方法;

  • Job能通过JobExecutionContext对象访问到Quartz运行时候的环境以及Job本身的明细数据。

  • 修改HelloJob类:

  • /**
     * @Author: Hjx
     * @Date: 2021/8/27 17:39
     */
    public class HelloJob implements Job {
        public HelloJob() {
            System.out.println("HelloJob任务被创建");
        }
    
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
    
            JobDetail jobDetail = context.getJobDetail();
            String jobDetailName = jobDetail.getKey().getName();
            System.out.println("任务名称:"+jobDetailName);
            String jobDetailGroup = jobDetail.getKey().getGroup();
            System.out.println("任务组名称:"+jobDetailGroup);
            String jobSimpleName = jobDetail.getJobClass().getSimpleName();
            System.out.println("任务实现类名称:"+jobSimpleName);
    
            Trigger trigger = context.getTrigger();
            String triggerName = trigger.getKey().getName();
            System.out.println("触发器名称:"+triggerName);
            String triggerGroup = trigger.getKey().getGroup();
            System.out.println("触发器组名称:"+triggerGroup);
            
            // 获取其他信息
            Date date = context.getFireTime();
            String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
            System.out.println("任务执行的时间"+format);
            Date nextFireTime = context.getNextFireTime();
            String format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(nextFireTime);
            System.out.println("下一次任务执行的时间"+format1);
    
            String string = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            System.out.println("数据库备份时间:"+string);
        }
    }
    
  • 运行结果

  • HelloJob任务被创建
    任务名称:JobDetailName
    任务组名称:JobDetailGroup
    任务实现类名称:HelloJob
    触发器名称:TriggerName
    触发器组名称:TriggerGroup
    任务执行的时间2021-08-30 11:00:02
    下一次任务执行的时间2021-08-30 11:02:30
    数据库备份时间:2021-08-30 11:00:02
    ······
    

6.5 JobDataMap

  • 在进行任务调度时,JobDataMap存储在JobExecutionContext中

  • JobDataMap可以用来装载任何可序列化的数据对象,当job实例对象被执行时这些参数对象会传递给它

  • JobDataMap实现了JDK的Map接口

  • 在这里插入图片描述

  • 修改TestQuartz类

  • /**
     * @Author: Hjx
     * @Date: 2021/8/27 17:05
     */
    public class TestQuartz {
        public static void main(String[] args) throws SchedulerException {
            // 1. 创建调度器Scheduler,从工厂中获取默认调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    
            // 2. 创建JobDetail实例
            // HelloJob.class 实现Job接口的实例类
            // "JobDetailName" JobDetail的名称,用来标识任务
            // "JobDetailGroup" 任务组名称,可省略,默认为DEFAULT
            // build() 创建JobDetail实例
    
            JobDataMap jobDataMap = new JobDataMap();
            jobDataMap.put("message","打印日志");
    
            JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                    .withIdentity("JobDetailName", "JobDetailGroup")
                    .usingJobData(jobDataMap)
                    .build();
    
            /*// 获取jobDetail设置的名称
            String jobDetailName = jobDetail.getKey().getName();
            System.out.println("设置的任务名称:"+jobDetailName);
            // 获取设置的任务组名称
            String jobDetailGroup = jobDetail.getKey().getGroup();
            System.out.println("设置的任务组名称:"+jobDetailGroup);
            // 获取Job实现类的名称
            String simpleName = jobDetail.getJobClass().getSimpleName();
            System.out.println("任务实例类名称:"+simpleName);*/
    
            // 3. 触发器 Trigger
            // "TriggerName" 触发器名称 用来标识触发器
            // "TriggerGroup" 触发器组名称,可省略,默认为DEFAULT
            // startNow() 开始时间 立刻开始
            // withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)) 每5秒执行一次
            // 创建触发器 Trigger实例
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity("TriggerName", "TriggerGroup")
                    .usingJobData(jobDataMap)
                    .startNow()
                    .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
                    .build();
    
            // 将任务与触发器相关联
            scheduler.scheduleJob(jobDetail, trigger);
    
            // 启动调度器
            scheduler.start();
        }
    }
    
  • 修改HelloJob类

  • /**
     * @Author: Hjx
     * @Date: 2021/8/27 17:39
     */
    public class HelloJob implements Job {
        public HelloJob() {
            System.out.println("HelloJob任务被创建");
        }
    
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
    
            /*JobDetail jobDetail = context.getJobDetail();
            String jobDetailName = jobDetail.getKey().getName();
            System.out.println("任务名称:"+jobDetailName);
            String jobDetailGroup = jobDetail.getKey().getGroup();
            System.out.println("任务组名称:"+jobDetailGroup);
            String jobSimpleName = jobDetail.getJobClass().getSimpleName();
            System.out.println("任务实现类名称:"+jobSimpleName);
    
            Trigger trigger = context.getTrigger();
            String triggerName = trigger.getKey().getName();
            System.out.println("触发器名称:"+triggerName);
            String triggerGroup = trigger.getKey().getGroup();
            System.out.println("触发器组名称:"+triggerGroup);*/
    
            // 获取jobDataMap
            JobDataMap jobDetailDataMap = context.getJobDetail().getJobDataMap();
            String JobDetailMessage = jobDetailDataMap.getString("message");
            System.out.println("任务信息:"+JobDetailMessage);
    
            JobDataMap triggerJobDataMap = context.getTrigger().getJobDataMap();
            String triggerMessage = triggerJobDataMap.getString("message");
            System.out.println("触发器信息:"+triggerMessage);
    
            String string = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            System.out.println("数据库备份时间:"+string);
        }
    }
    
  • 运行结果

  • HelloJob任务被创建
    任务信息:打印日志
    触发器信息:打印日志
    数据库备份时间:2021-08-30 10:44:24
    ······
    
  • Job实现类中添加setter方法对应JobDataMap的键值,Quartz框架默认的JobFactory实现类在初始化job实例对象时会自动地调用这些setter方法。

  • /**
     * @Author: Hjx
     * @Date: 2021/8/27 17:05
     */
    public class TestQuartz {
        public static void main(String[] args) throws SchedulerException {
            // 1. 创建调度器Scheduler,从工厂中获取默认调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    
            // 2. 创建JobDetail实例
            // HelloJob.class 实现Job接口的实例类
            // "JobDetailName" JobDetail的名称,用来标识任务
            // "JobDetailGroup" 任务组名称,可省略,默认为DEFAULT
            // build() 创建JobDetail实例
    
    
            JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                    .withIdentity("JobDetailName", "JobDetailGroup")
                    .usingJobData("message","任务信息")
                    .build();
    
            /*// 获取jobDetail设置的名称
            String jobDetailName = jobDetail.getKey().getName();
            System.out.println("设置的任务名称:"+jobDetailName);
            // 获取设置的任务组名称
            String jobDetailGroup = jobDetail.getKey().getGroup();
            System.out.println("设置的任务组名称:"+jobDetailGroup);
            // 获取Job实现类的名称
            String simpleName = jobDetail.getJobClass().getSimpleName();
            System.out.println("任务实例类名称:"+simpleName);*/
    
            // 3. 触发器 Trigger
            // "TriggerName" 触发器名称 用来标识触发器
            // "TriggerGroup" 触发器组名称,可省略,默认为DEFAULT
            // startNow() 开始时间 立刻开始
            // withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)) 每5秒执行一次
            // 创建触发器 Trigger实例
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity("TriggerName", "TriggerGroup")
                    .usingJobData("message","触发器信息")
                    .startNow()
                    .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
                    .build();
    
            // 将任务与触发器相关联
            scheduler.scheduleJob(jobDetail, trigger);
    
            // 启动调度器
            scheduler.start();
        }
    }
    
  • /**
     * @Author: Hjx
     * @Date: 2021/8/27 17:39
     */
    public class HelloJob implements Job {
    
        private String message;
    
        public HelloJob() {
            System.out.println("HelloJob任务被创建");
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
    
            /*JobDetail jobDetail = context.getJobDetail();
            String jobDetailName = jobDetail.getKey().getName();
            System.out.println("任务名称:"+jobDetailName);
            String jobDetailGroup = jobDetail.getKey().getGroup();
            System.out.println("任务组名称:"+jobDetailGroup);
            String jobSimpleName = jobDetail.getJobClass().getSimpleName();
            System.out.println("任务实现类名称:"+jobSimpleName);
    
            Trigger trigger = context.getTrigger();
            String triggerName = trigger.getKey().getName();
            System.out.println("触发器名称:"+triggerName);
            String triggerGroup = trigger.getKey().getGroup();
            System.out.println("触发器组名称:"+triggerGroup);*/
    
            /*// 获取jobDataMap
            JobDataMap jobDetailDataMap = context.getJobDetail().getJobDataMap();
            String JobDetailMessage = jobDetailDataMap.getString("message");
            System.out.println("任务信息:"+JobDetailMessage);
    
            JobDataMap triggerJobDataMap = context.getTrigger().getJobDataMap();
            String triggerMessage = triggerJobDataMap.getString("message");
            System.out.println("触发器信息:"+triggerMessage);*/
    
            /*// 获取其他信息
            Date date = context.getFireTime();
            String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
            System.out.println("任务执行的时间"+format);
            Date nextFireTime = context.getNextFireTime();
            String format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(nextFireTime);
            System.out.println("下一次任务执行的时间"+format1);*/
    
            System.out.println(message);
    
            String string = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
            System.out.println("数据库备份时间:"+string);
        }
    }
    
  • 成员的类型和名称需要和在TestQuartz中设置的对应

  • jobDetail和trigger的jobDataMap的key值相同,trigger的jobDataMap会覆盖jobDetail的

6.6 有状态的Job和无状态的Job

  • @PersistJobDataAfterExecution注解的使用

    • 有状态的Job可以理解为多次job调用期间可以持有一些状态信息,这些状态信息存储在JobDataMap中
    • 默认的无状态job每次调用时都会创建一个新的JobDataMap。
  • /**
     * @Author: Hjx
     * @Date: 2021/8/30 11:19
     */
    public class TestQuartz01 {
        public static void main(String[] args) throws SchedulerException {
            // 1. 获取调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    
            // 2. 获取JobDetail
            JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
                    .withIdentity("JobDetail", "JobDetailGroup")
                    .usingJobData("count",0)
                    .build();
    
            // 3. 获取trigger
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity("Trigger", "TriggerGroup")
                    .startNow()
                    .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
                    .build();
    
            // 4. 通过调度器绑定任务和触发器
            scheduler.scheduleJob(jobDetail,trigger);
    
            //5. 启动
            scheduler.start();
    
        }
    }
    
  • /**
     * @Author: Hjx
     * @Date: 2021/8/30 11:25
     */
    public class TestJob implements Job {
    
        private Integer count;
    
        public void setCount(Integer count) {
            this.count = count;
        }
    
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("TestJob");
    
            count++;
            JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
            jobDataMap.put("count",count);
            System.out.println("TestJob中的count值:"+count);
            System.out.println("jobDataMap的count值:"+jobDataMap.getInt("count"));
        }
    }
    
  • TestJob
    TestJob中的count值:1
    jobDataMap的count值:1
    TestJob
    TestJob中的count值:1
    jobDataMap的count值:1
    ······
    
  • 添加@PersistJobDataAfterExecution注解

  • /**
     * @Author: Hjx
     * @Date: 2021/8/30 11:25
     */
    @PersistJobDataAfterExecution
    public class TestJob implements Job {
    
        private Integer count;
    
        public void setCount(Integer count) {
            this.count = count;
        }
    
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("TestJob");
    
            count++;
            JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
            jobDataMap.put("count",count);
            System.out.println("TestJob中的count值:"+count);
            System.out.println("jobDataMap的count值:"+jobDataMap.getInt("count"));
        }
    }
    
  • TestJob
    TestJob中的count值:1
    jobDataMap的count值:1
    TestJob
    TestJob中的count值:2
    jobDataMap的count值:2
    TestJob
    TestJob中的count值:3
    jobDataMap的count值:3
    ······
    

代码参考地址:[Spring/Quartz · 萝卜7/点点滴滴汇成河 - 码云 - 开源中国 (gitee.com)](

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值