BestJaxXu

导航

 

1、Quartz学习的核心概念

任务Job

job就是你想实现的任务类,每一个job必须实现org.quartz.job接口,且只需实现接口定义的execute()方法

触发器Trigger
Trigger为你执行任务的触发器(定时)
Trigger主要包含两个SimpleTrigger和CronTrigger两种

调度器Scheduler
Scheduler为定时任务调度,它会将任务Job及触发器Trigger整合起来,负责基于Trigger设定的时间来执行Job

2、Quartz的体系结构

3、Quartz的几个常用API

Scheduler:用于定时调度程序交互的主程序接口。Scheduler调度程序:任务执行计划表,只有安排进执行计划的任务Job【通过scheduler.scheduleJob方法安排进执行任务】,当它预先定义的执行时间到了的时候(任务出发trigger)该任务才会被执行

Job:预先定义的,希望在未来某个使劲能被调度程序执行的任务类,可自定义

JobDetail:使用JobDetail来定义定时任务的实例,JobDetail实例是通过JobBuilder类创建的

JobDataMap:可以包含不限量(序列化的)的数对象,在Job实例执行的时候,可以使用其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型数据的方法

Trigger:触发器,Trigger对象是用来触发执行Job的。当调度一个Job时,我们实例一个触发器然后调整他的属性来满足Job执行的条件。表明任务在什么时候会执行。【定义了一个已经被安排的任务将会在什么时候执行的时间条件】

JobBuilder:用于声明一个任务实例,也可以定义关于该任务的详情比如任务名,组名等。通过JobBuilder声明的实例将会作为一个实际执行的任务。

TriggerBuilder:触发器创建器,用于创建触发器trigger实例

JobListener、TriggerListener、SchedulerListener监听器,用于对组件的监听

4、入门案例

4.1:pom.xml

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

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

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
</dependency>

4.2任务类。


@Slf4j
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()) + " | 任务被执行了");
    }
}

4.3调度器


public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException {
        //从调度工厂中获取调度器实例
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //通过JobBuilder构建一个任务实例
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                //设置任务的唯一实例名称和任务组名称组名
                .withIdentity("job1", "group1")
                //构建实例
                .build();
        //通过TriggerBuilder构建触发器实例
        SimpleTrigger trigger = TriggerBuilder.newTrigger()
                //设置触发器唯一实例名称和触发器的组名
                .withIdentity("trigger1", "group1")
                //执行计划,每五秒执行一次
                .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
                //立即执行
                .startNow()
                //构建实例
                .build();
        //调度器绑定任务实例和触发器
        scheduler.scheduleJob(jobDetail,trigger);
        //开启定时任务
        scheduler.start();
    }
}

4.4执行结果

5.Job和JobDetail的关系介绍

Job:工作任务调度接口,任务类需要实现的接口。该接口中定义了execute方法,类似JDK提供的TimeTask类的run方法。在这里面编写任务执行的业务逻辑
Job实例在Quartz中的生命周期:每次调度器执行Job时,它在调用execute方法前会创建一个新的Job实例,当调度完成后,管理的Job对象实例将会被释放,释放的实例会被垃圾回收机制回收

@Slf4j
public class HelloJob implements Job {

    public HelloJob(){
        log.info("实例被创建了");
    }

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()) + " | 任务被执行了");
    }
}

执行结果:

JobDetail:JobDetail为Job实例提供了许多设置属性,已经JobDetaMap成员变量属性,它用来存储特定Job实例的状态信息,调度器需要借助于JobDetail对象来添加Job实例【JobDetail中重要的属性:name[任务实例的唯一标识名称]、group[任务调度实例的组名]、jobClass[任务类信息]、jobDataMap】

@Slf4j
public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException {
        //从调度工厂中获取调度器实例
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //通过JobBuilder构建一个任务实例
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                //设置任务的唯一实例名称和任务组名称组名
                .withIdentity("job1", "group1")
                //构建实例
                .build();
        //jobDataMap
        JobDataMap jobDataMap = jobDetail.getJobDataMap();
        //任务实例的唯一标识名称
        String name = jobDetail.getKey().getName();
        //任务调度实例的组名
        String group = jobDetail.getKey().getGroup();
        //任务类信息
        String clazzName = jobDetail.getKey().getClass().getName();
        log.info("jobDataMap:{}", jobDataMap);
        log.info("任务实例的唯一标识名称:"+name);
        log.info("任务调度实例的组名:"+group);
        log.info("任务类信息:"+clazzName);
        //通过TriggerBuilder构建触发器实例
        SimpleTrigger trigger = TriggerBuilder.newTrigger()
                //设置触发器唯一实例名称和触发器的组名
                .withIdentity("trigger1", "group1")
                //执行计划,每五秒执行一次
                .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
                //立即执行
                .startNow()
                //构建实例
                .build();
        //调度器绑定任务实例和触发器
        scheduler.scheduleJob(jobDetail,trigger);
        //开启定时任务
        scheduler.start();
    }
}

执行结果

6、JobExecutionContext的介绍

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

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

获取JobDetail相关信息、获取Trigger相关信息、获取Job类本身的信息


@Slf4j
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        //通过JobExecutionContext获取JobDetail
        JobDetail jobDetail = jobExecutionContext.getJobDetail();
        JobKey jobKey = jobDetail.getKey();
        String jobDetailName = jobKey.getName();
        String jobDetailGroup = jobKey.getGroup();
        String jobClazzNameJobDetail = jobKey.getClass().getName();
        log.info("任务实例的唯一标识名称:" + jobDetailName);
        log.info("任务实例的组名:" + jobDetailGroup);
        log.info("任务实例绑定的任务类信息:" + jobClazzNameJobDetail);
        log.info("-----------------------------------------");
        //通过JobExecutionContext获取Trigger
        Trigger trigger = jobExecutionContext.getTrigger();
        TriggerKey triggerKey = trigger.getKey();
        String triggerKeyName = triggerKey.getName();
        String triggerKeyGroup = triggerKey.getGroup();
        String triggerClazzName = triggerKey.getClass().getName();
        log.info("触发器的唯一标识名称:" + triggerKeyName);
        log.info("触发器的组名:" + triggerKeyGroup);
        log.info("触发器绑定的任务类信息:" + triggerClazzName);
        log.info("-----------------------------------------");
        //通过JobExecutionContext
        String jobClazzName = jobExecutionContext.getClass().getName();
        log.info("job类相关的信息:"+jobClazzName);
        log.info("-----------------------------------------");
        log.info(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()) + " | 任务被执行了");
    }
}

7、JobDataMap介绍

使用Map获取
在进行任务调度时,JobDataMap存储在JobExecutionContent中,非常方便获取
JobDataMap可以用来存储任何可序列化的数据对象,当Job实例对象被执行时这些参数都会传递给它
JobDataMap实现了JDK的Map接口,并且添加了非常方便的方法来存储基本数据类型

8、有状态的Job和无状态的Job

什么是有状态的Job?什么是无状态的Job?

有状态的Job可以理解为多次Job调用期间可以持有一些状态信息,这些状态信息存储在JobDataMap中,而默认的无状态Job每次调用时都会创建一个新的JobDataMap

Job默认是无状态的,通过如下示例查看:

@Slf4j
public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException {
        //从调度工厂中获取调度器实例
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //通过JobBuilder构建一个任务实例
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                //设置任务的唯一实例名称和任务组名称组名
                .withIdentity("job1", "group1")
                //设置jobDataMap数据   <<===============
                .usingJobData("count",0)
                //构建实例
                .build();
        //通过TriggerBuilder构建触发器实例
        SimpleTrigger trigger = TriggerBuilder.newTrigger()
                //设置触发器唯一实例名称和触发器的组名
                .withIdentity("trigger1", "group1")
                //执行计划,每五秒执行一次
                .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
                //立即执行
                .startNow()
                //构建实例
                .build();
        //调度器绑定任务实例和触发器
        scheduler.scheduleJob(jobDetail,trigger);
        //开启定时任务
        scheduler.start();
    }
}
@Slf4j
public class HelloJob implements Job {
    private Integer count;

    public void setCount(Integer count) {
        this.count = count;
    }

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        //通过JobExecutionContext获取JobDetail
        JobDetail jobDetail = jobExecutionContext.getJobDetail();
        count++;
        jobDetail.getJobDataMap().put("count",count);
        log.info(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()) + " | 任务被执行了");
        log.info("当前count的值为:" + count);
    }
}

通过@PersistJobDataAfterExecution注解将Job设置为由状态的

@Slf4j
@PersistJobDataAfterExecution
public class HelloJob implements Job {
    private Integer count;

    public void setCount(Integer count) {
        this.count = count;
    }

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        //通过JobExecutionContext获取JobDetail
        JobDetail jobDetail = jobExecutionContext.getJobDetail();
        count++;
        jobDetail.getJobDataMap().put("count",count);
        log.info(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()) + " | 任务被执行了");
        log.info("当前count的值为:" + count);
    }
}

如果Job类没有添加@PersistJobDataAfterExecution注解,每次调用时都会创建一个新的JobDataMap。不会累加count的值
如果Job类添加了@PersistJobDataAfterExecution注解,每次调用期间都会持有一些状态信息,。会累加count的值

9、Trigger触发器的介绍

上图即是Quartz的触发器类型,常用的为SimpleTriggerImpl,CronTriggerImpl

12.1.SimpleTriggerImpl的使用

设置触发的开始时间和结束时间及执行计划


@Slf4j
@PersistJobDataAfterExecution
public class HelloJobTrigger implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        Trigger trigger = jobExecutionContext.getTrigger();
        JobKey jobKey = trigger.getJobKey();
        log.info("触发器名称:" + jobKey.getName() + " | 触发器组名:" + jobKey.getGroup() );
        log.info("触发器开始执行时间:" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(trigger.getStartTime()) + " | 触发器结束执行时间:" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(trigger.getEndTime()));
        log.info(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()) + " | 任务被执行了");
    }
}


@Slf4j
public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException {
        //开始时间 & 结束时间
        Date startTime = new Date();
        Date endTime = new Date();
        endTime.setTime(endTime.getTime() + 20000);
        //从调度工厂中获取调度器实例
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //通过JobBuilder构建一个任务实例
        JobDetail jobDetail = JobBuilder.newJob(HelloJobTrigger.class)
                //设置任务的唯一实例名称和任务组名称组名
                .withIdentity("job1", "group1")
                //构建实例
                .build();
        //通过TriggerBuilder构建触发器实例
        SimpleTrigger trigger = TriggerBuilder.newTrigger()
                //设置触发器唯一实例名称和触发器的组名
                .withIdentity("trigger1", "group1")
                //执行计划,每五秒执行一次
                .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)
                               .withRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY) //重复执行次数
                             )
                //开始执行时间
                .startAt(startTime)
                //结束执行时间
                .endAt(endTime)
                //构建实例
                .build();
        //调度器绑定任务实例和触发器
        scheduler.scheduleJob(jobDetail,trigger);
        //开启定时任务
        scheduler.start();
    }
}

注意:

SimpleTrigger的属性有:开始时间,结束时间,重复次数和重复时间间隔
重复次数的值可以为0、正数、或常量。例如:SimpleTrigger.REPEAT_INDEFINITELY
重复的时间间隔属性值必须为大于0或者长整型的整数,以毫秒为为时间单位,当重复的时间间隔为0时,意味着与Trigger同时触发执行
如果有指定结束时间属性值,则结束时间属性优先于重复次数属性,这样的好处在于;当我们需要创建一个每间隔10秒触发一次直到指定的结束时间的Trigger,而无需去计算从开始到结束的所重复执行次数。我们只需要简单的执行结束时间和使用REPEAT_INDEFINITELY作为重复次数的属性值即可

10CronTrigger触发器的介绍

13.1.CronTrigger简介
如果需要像日历那样按日程来触发任务,而不是像SimpleTrigger那样每隔特定的间隔时间触发,CronTriggers通常比SimpleTrigger更有用,因为它是基于日历的任务调度器

使用CronTrigger,可以指定诸如每个周五的12点,或者每天的9点等等…这样的日程来安排触发。甚至还可以像SimpleTrigger一样,CornTrigger也有一个startTime以指定日程从什么时候开始,也有一个(可选的)endTime以指定任何日期不再继续
13.2.corn表达式
corn表达式相信大家并不陌生,corn表达式被用来配置CronTrigger实例。corn表达式是一个由7个表达式组成的字符串。每一个子表达式描述了一个单独的日程细节。这些子表达式用空格分隔,分别表示:seconds秒、minutes分钟、hours小时、day-of-month月中的天、moth月,year年
在线生成corn表达式:https://cron.qqe2.com/

以上信息全部来自以下这篇文章,这位大佬写的特别棒,该记录仅作个人学习笔记使用,如有侵权请联系我立即下架!

(198条消息) 分布式定时任务调度框架【Quartz】学习与实战记录完整篇_分布式定时任务quartz_Tang.Mr的博客-CSDN博客

posted on 2023-06-02 16:06  BestJaxXu  阅读(61)  评论(0编辑  收藏  举报