Quartz任务调度快速入门进阶二——架构核心分析

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

架构核心分析

SchedulerFactory (调度程序工厂):

  1. StdSchedulerFactory:Quartz默认的SchedulerFactory (默认实例化new StdSchedulerFactory()
  2. DirectSchedulerFactory:DirectSchedulerFactory是对SchedulerFactory的直接实现,通过它可以直接构建Scheduler、threadpool等
    1. ThreadExecutor / DefaultThreadExecutor:内部线程操作对象;

JobExecutionContext:Job上下文

  1. 保存着Trigger、JobDeaitl等信息,Job的execute方法传递的参数就是对象的实例;
  2. JobExecutionContextImpl

Scheduler:调度器

  1. StdScheduler:Quartz默认的Scheduler
  2. RemoteScheduler:带有RMI功能的Scheduler

Job:任务对象

  1. JobDetail:实现轮询的一个回调类,可将参数封装成JobDataMap对象,Quartz将任务的作业状态保存在JobDetail中
  2. JobDataMap:JobDataMap用来报错由JobDetail传递过来的任务实例对象

Trigger:触发器

  1. SimpleTrigger <普通的Trigger> :SimpleScheduleBuilder
  2. CronTrigger <带Cron Like 表达式的Trigger> :CronScheduleBuilder
  3. CalendarIntervalTrigger <带日期触发的Trigger>:CalendarIntervalScheduleBuilder
  4. DailyTimeIntervalTrigger <按天触发的Trigger> :DailyTimeIntervalScheduleBuilder

ThreadPool:为Quartz运行任务时提供了一些线程

  1. SimpleThreadPool:一个Quartz默认实现的简单线程池,它足够健壮,能够应对大部分常用场景

xxxListener:监听器

  1. JobListener
  2. TriggerListener
  3. SchedulerListener

入门案例

创建HelloJob任务类

HelloJob.java

//定义任务类
public class HelloJob implements Job{
    
    @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
            // 定义时间
            Date date = new Date();
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = dateFormat.format(date);
            // 定义工作任务内容
            System.out.println("进行数据库备份操作,当前任务执行时间:"+dateString);
    }
}
复制代码

创建任务调度类HelloSchedulerDemo

HelloSchedulerDemo.java

public class HelloSchedulerDemo {
        public static void main(Stringp[] args) throws Exception{
            //1:从工厂中获取任务调度的实例
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            
            //2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
            JobDetail job = JobBuilder.newJob(HelloJob.class)
                        .withIdentity("job1", "group1") //定义该实例唯一标识
                        .build();
                   
            //3:定义触发器,马上执行,然后每5秒重复执行一次
            Trigger trigger = TriggerBuilder.newTrigger()
                        .withIdentity("trigger1", "group1") // 定义该实例唯一标识
                        .startNow() // 马上执行
                        .withScheduler(SimpleSchedulerBuilder.simpleScheduler()
                                .repeatSecondlyForever(5)) // 每5秒执行一次
                        .build();
                        
            //4:使用触发器调度任务的执行
            scheduler.schedulerJob(job, trigger);
            
            //5:开启
            scheduler.start();
            
            //6:关闭
            scheduler.shutdown();
        }
}
复制代码

Job和JobDetail介绍

  • Job:工作任务调度的接口,任务类需要实现该接口。该接口中定义execute方法,类似JDK提供的TimeTask类的run方法。在里面编写任务执行的业务逻辑。
  • Job实例在Quartz中的生命周期:每次调度器执行Job时,它在调用execute方法前会创建一个新的Job实例,当调用完成后,关联的Job对象实例会被释放,释放的实例会被垃圾回收机制回收。
  • JobDetail:JobDetail为Job实例提供了许多设置属性,以及JobDataMap成员变量属性,它用来存储特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例。
  • JobDetail重要属性:name、group、jobClass、jobDataMap。
JobDetail job = JobBuilder.newJob(HelloJob.class)
            .withIdentity("job1", "group1") // 定义该实例唯一标识,并指定一个组
            .build();
            
System.out.println("name:"+job.getKey().getName());
System.out.println("group:"+job.getKey().getGrouop());
System.out.println("jobClass:"+job.getJobClass().getName());
复制代码

JobExecutionContext介绍

  • 当Scheduler调用一个Job,就会将JobExecutionContext传递给Job的execute()方法;
  • Job能通过JobExecution Context对象访问到Quartz运行时候的环境以及Job本身的明细数据。

HelloJob.java

@Override
public void execute(JobExecutionContext context) throws JobExecutionException{
    ...
    // 获取JobDetailde内容
    JobKey jobKey = context.getJobDetail().getKey();
    System.out.println("工作任务名称:"+jobKey.getName()+",工作任务的组"+jobKey.getGroup());
    System.out.println("任务类的名称(带路径):"+context.getJobDetail().getJobClass().getName());
    System.out.println("任务类的名称:"+context.getJobDetail().getJobClass().getSimpleName());
    // 获取Trigger的内容
    TriggerKey triggerKey = context.getTrigger().getKey();
    System.out.println("触发器名称:"+triggerKey.getName()+",触发器的组"+triggerKey.getGroup());
    
    // 获取其他内容
    System.out.println(context.getJobRunTime()); // 获取任务运行时间,long类型
    System.out.println(context.getFireTime()); // 获取当前任务的执行时间,Date类型
    System.out.println(context.getNextFireTime()); // 获取下一次任务的执行时间,Date类型
    ...
}
复制代码

JobDataMap介绍

1、使用Map获取

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

HelloSchedulerDemo.java

// 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
JobDetail job = JobBuilder.newJob(HelloJob.class)
            .withIdentity("job1", "group1") 
            .usingJobData("message", "打印日志") // 传递参数:名称message
            .build();

// 3:定义触发器,马上执行,然后每5秒重复执行一次
Trigger trigger = TriggerBuilder.newTrigger()
            .withIdentity("trigger1", "group1") 
            .startNow()
            .withScheduler(SimpleSchedulerBuilder.simpleScheduler()
                                .repeatSecondlyForever(5))
             .usingJobData("message", "simple触发器") // 传递参数:名称message
             .build();
复制代码

HelloJob.java

@Override
public void execute(JobExecutionContext context) throws JobExecutionException{
    ...
    // 获取传递的参数,从JobDetail中获取
    JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
    String jobDataMessage = jobDataMap.getString("message");
    System.out.println("任务数据的参数值:"+jobDataMessage);
    // 获取传ide参数,从Trigger中获取
    JobDataMap triggerDataMap = context.getTrigger().getJobDataMap();
    String triggerDataMessage = triggerDataMap.getString("message");
    System.out.println("触发器数据的参数值:"+triggerDataMessage);
    ...
}
复制代码

2、Job实现类中添加setter方法对应JobDataMap的键值,Quartz框架默认的JobFactory实现类在初始化Job实例对象时会自动调用这些setter方法

HelloJob.java

private String message;

public void setMessage(String message){
    this.message = message;
}
复制代码

注意:使用这种方式,如果遇到同名的key,Trigger中的usingJobData("message", "simple触发器")会覆盖JobDetail中的usingJobData("message", "打印日志")

有状态的Job和无状态的Job

@PersistJobDataAfterExecution注解的使用

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

1、修改HelloSchedulerDemo.java,添加 .usingJobData("count", 0),标识计数器

JobDetail job = JobBuilder.newJob(HelloJob.class)
            .withIdentity("job1", "group1") 
            .usingJobData("message", "打印日志") // 传递参数:名称message
            .usingJobData("count", 0)
            .build();
复制代码

2、修改HelloJob.java 添加count的setter方法

private Integer count;
public void setCount(Integer count){
    this.count = count;
}
复制代码

在execute方法中添加下面部分

++count;
System.out.println("count的数量:"+count);
context.getJobDetail().getJobDataMap().put("count", count);
复制代码

HelloJob类没有添加@PersistJobDataAfterExecution注解,每次调用时都会创建一个新的JobDataMap,count不会累加;

HelloJob类添加@PersistJobDataAfterExecution注解,多次Job调用期间可以持有一些状态信息,即可以实现count的累加。

猜你喜欢

转载自juejin.im/post/7098163875496853511