Quartz定时任务的组件API

个人博客:https://domyselfzy.coding.me/

本讲主要说明Quartz中重要的几个组件的API。

Scheduler(调度器)

Scheduler的生命期,从SchedulerFactory创建它时开始,到Scheduler调用shutdown()方法时结束;Scheduler被创建后,可以增加、删除和列举Job和Trigger,以及执行其它与调度相关的操作(如暂停Trigger)。但是,Scheduler只有在调用start()方法后,才会真正地触发trigger(即执行job)。

上面说了,Scheduler是由Factory创建的,有两种方式来创建一个Scheduler。

  1. 直接获取
// 1. 创建 SchedulerFactory
SchedulerFactory factory = new StdSchedulerFactory();
// 2. 从工厂中获取调度器实例
Scheduler scheduler = factory.getScheduler();
  1. 自己配置FactoryBean
/**
 * 

初始化一个构建Scheduler的工厂

*/
@Bean public SchedulerFactoryBean schedulerFactoryBean() throws Exception { SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setQuartzProperties(quartzProperties()); factory.setOverwriteExistingJobs(true); factory.afterPropertiesSet(); return factory; } /** * 通过SchedulerFactoryBean获取Scheduler的实例 */ @Bean public Scheduler scheduler() throws Exception { Scheduler scheduler = schedulerFactoryBean().getScheduler(); if (scheduler.isStarted()) { scheduler.shutdown(); } scheduler.start(); return scheduler; }

第二种方式,有一个好处,可以初始化指定的配置文件,也不一定必须放在ClassPath的指定位置。

Job

一个job就是一个实现了Job接口的类,该接口只有一个方法:

public interface Job {

public void execute(JobExecutionContext context)
  throws JobExecutionException;
}

简单来说,Job的使命就是你将需要执行的任务逻辑,写在execute()方法中。
当一个定时任务被执行时,execute()方法由调度程序的一个线程调用。
我们可以在Job中定义自己当前作业需要执行的逻辑,如:

@Override
@SuppressWarnings("unchecked")
@Transactional(rollbackFor = Exception.class)
public void execute(JobExecutionContext jobExecutionContext) {
    JobDetail jobDetail = jobExecutionContext.getJobDetail();
    JobDataMap jobDataMap = jobDetail.getJobDataMap();
   
    messageTemplate = (RedisTemplate<String, String>) jobDataMap.get("messageTemplate");
    scheduler = (Scheduler) jobDataMap.get("scheduler");

    //处理分布式的锁问题
    String lockName = QuartzConstants.LOCK_KEY + QuartzConstants.JMS_JOIN_SIGN + task.getId();
    Boolean absent = messageTemplate.opsForValue().setIfAbsent(lockName, "task-job-lock");
    try {
        if (BooleanUtils.isNotTrue(absent)) {
            logger.info("Get lock fail, current job is executing , please wait next invoke , task {}"
                    , task.toString());
            return;
        }

        //避免程序服务挂掉,导致锁无法释放,设定20s,避免定时任务相距时间太短,如果有问题,再调整
        messageTemplate.expire(lockName, 20, TimeUnit.SECONDS);
    } catch (Exception e) {
        e.printStackTrace();
        logger.error("send message error, task: {}", task.toString());
    } finally {
        if (BooleanUtils.isTrue(absent)) {
            logger.info("Free lock");
            messageTemplate.delete(lockName);
        }
    }
}

上面代码,就定义了一个我自己的逻辑,处理多机集群时,同步触发的任务锁。

JobExecutionContext

这个对象实例,将包含作业执行过程的上下文信息,可以在执行当前任务过程中获取相应信息。同时,你也可以在创建一个定时任务时将自己需要的信息放入到该上下文中。
**注意:**JobExecutionContext对象中保存着该job运行时的一些信息 ,执行job的scheduler的引用,触发job的trigger的引用,JobDetail对象引用,以及一些其它信息。

JobDetail(实例化作业)

上面说的Job,其实并不是真正可以执行的作业任务,它只是定义了一个作业能做什么,需要做什么事。真正需要被任务调度的其实的一个实例化Job的JobDetail。你可以有两种理解方式,一种是Job是一个父类,JobDetail是它的实现类(当然实际并不是,这里只是方便理解),二是,JobDetail类似Java中Number这样的包装类,将Job包装了一次,并且封装了更多的信息。

如何实例化一个JobDetail呢?

// define the job and tie it to our HelloJob class
JobDetail job = newJob(HelloJob.class)
  .withIdentity("myJob", "group1") // name "myJob", group "group1"
  .build();

很简单,只需要将你需要执行的一个Job,直接丢给JobDetail就可以了。
但是要特别注意就是,你传入个Map中的对象序列号的问题,建议采用Java标准序列化。
除此之外,上文也说了,JobExecutionContext不是还可以包含一些自定义信息吗?,在这里就可以定义了。

JobDataMap

在JobDetail中,可以使用JobDataMap来设置更多你需要带入到Job中的信息。
构建JobDetail时,可以将数据放入Map中

// define the job and tie it to our DumbJob class
JobDetail job = newJob(DumbJob.class)
  .withIdentity("myJob", "group1") // name "myJob", group "group1"
  .usingJobData("jobSays", "Hello World!")
  .usingJobData("myFloatValue", 3.141f)
  .build();

在Job执行过程中,可以从中取出来相应数据。

public class DumbJob implements Job {

    public DumbJob() {
    }

    public void execute(JobExecutionContext context)
      throws JobExecutionException
    {
      JobKey key = context.getJobDetail().getKey();

      JobDataMap dataMap = context.getJobDetail().getJobDataMap();

      String jobSays = dataMap.getString("jobSays");
      float myFloatValue = dataMap.getFloat("myFloatValue");

      System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
    }
  }

好了,看到这里,不知道大家有没有疑惑?为啥好多地方都定义了withIdentity这个语句?

这里说明一下Quartz中Key的作用

Key(Job或者Trigger的唯一表示)

简答来说,不管是Job还是Trigger都要有个名字,Quartz还可以对其进行分组,这两样加起来正好作为了它的唯一标识。

将Job和Trigger注册到Scheduler时,可以为它们设置key,配置其身份属性。Job和Trigger的key(JobKey和TriggerKey)可以用于将Job和Trigger放到不同的分组(group)里,然后基于分组进行操作。同一个分组下的Job或Trigger的名称必须唯一,即一个Job或Trigger的key由名称(name)和分组(group)组成。

你可能感兴趣的:(Java,框架)