Quartz框架来实现定时任务

  在开发过程当中,须要实现定时来执行某些方法任务,这时可使用Quartz框架来实现这个功能。mysql

一 Quartz简单使用

  Quartz中主要包含几个核心概念,以下:sql

  1. Job 表示一个工做,要执行的具体内容。此接口中只有一个方法,以下:
    void execute(JobExecutionContext context) 
  2. JobDetail 表示一个具体的可执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。 
  3. Trigger 表明一个调度参数的配置,何时去调。 
  4. Scheduler 表明一个调度容器,一个调度容器中能够注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就能够被 Scheduler 容器调度了。 

 1.1 配置Scheduler

  上文说Scheduler是一个调度容器,任意一个JobDetail和任意一个Trigger结合为一对便可进行注册,而Scheduler须要实例化,只有在实例化之后,才能执行他的启动(start)、暂停(stand-by)、中止(shutdown)方法。app

注意:scheduler被中止后,除非从新实例化,不然不能从新启动;只有当scheduler启动后,即便处于暂停状态也不行,trigger才会被触发(job才会被执行)。框架

 1 SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
 2 
 3   Scheduler sched = schedFact.getScheduler();
 4 
 5   sched.start();
 6 
 7   // define the job and tie it to our HelloJob class
 8   JobDetail job = newJob(HelloJob.class)
 9       .withIdentity("myJob", "group1")
10       .build();
11 
12   // Trigger the job to run now, and then every 40 seconds
13   Trigger trigger = newTrigger()
14       .withIdentity("myTrigger", "group1")
15       .startNow()
16       .withSchedule(simpleSchedule()
17           .withIntervalInSeconds(40) //每40s执行一次
18           .repeatForever())
19       .build();
20 
21   // Tell quartz to schedule the job using our trigger
22   sched.scheduleJob(job, trigger);

1.2 实现jobDetail

  具体的工做类须要实现接口Job,接口须要实现一个execute方法,而具体的job要执行的任务,就写在execute方法中。以下:  ide

public class HelloJob implements Job {

    public HelloJob() {
    }

    public void execute(JobExecutionContext context)
      throws JobExecutionException
    {
      System.err.println("Hello!  HelloJob is executing.");
    }
  }

 

二 Quartz和Spring boot结合

  建立一个监听类来实现配置Scheduler,使Spring boot在启动时自动加载该类,开始定时任务。以下:函数

 1 @Component
 2 public class TimedRegister implements ApplicationListener<ApplicationReadyEvent> {
 3 
 4     @Autowired
 5     FindMessage findMessage;
 6 
 7     @Override
 8     public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
 9         try {
10             // Grab the Scheduler instance from the Factory
11             Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
12 
13             // and start it off
14             scheduler.start();
15 
16             // define the job and tie it to our HelloJob class
17             JobDetail mysqlDoJob = JobBuilder.newJob(MysqlTimedSendJob.class)
18                     .withIdentity("job1", "jobGroup1")
19                     .build();
20 
21             mysqlDoJob.getJobDataMap().put("findMessage", findMessage);
22 
23             Trigger mysqlTrigger = newTrigger()
24                     .withIdentity("trigger1", "triggerGroup1")
25                     .withSchedule(CronScheduleBuilder.cronSchedule("0 0 10 * * ?"))
26                     .build();
27 
28             scheduler.scheduleJob(mysqlDoJob, mysqlTrigger);
29 
30         } catch (Exception e) {
31             logger.error("TimedRegister daily job error", e);
32         }
33     }
34 }

  以上须要注意几点:ui

  1. ApplicationListener接口中须要传入监听参数(ApplicationReadyEvent),由于若是不传入参数的话,会对每一个event都进行监听,则会发生同时执行好几个定时任务这样的惨状。这个问题不仅存在与定时任务的监听。spa

  2. 先看一段解释:code

  咱们传给scheduler一个JobDetail实例,由于咱们在建立JobDetail时,将要执行的job的类名传给了JobDetail,因此scheduler就知道了要执行何种类型的job;每次当scheduler执行job时,在调用其execute(…)方法以前会建立该类的一个新的实例;执行完毕,对该实例的引用就被丢弃了,实例会被垃圾回收;这种执行策略带来的一个后果是,job必须有一个无参的构造函数(当使用默认的JobFactory时);另外一个后果是,在job类中,不该该定义有状态的数据属性,由于在job的屡次执行中,这些属性的值不会保留。那么如何给job实例增长属性或配置呢?如何在job的屡次执行中,跟踪job的状态呢?答案就是:JobDataMap,JobDetail对象的一部分。对象

  由于job的具体类不是Spring建立的,而是quartz建立的,因此不能经过注入的方式来调用findmessage,只能经过将这个findmessage建立对象后经过参数注入的方式由JobDataMap来传入Job具体的实现中。因此咱们在jobdetail中添加参数。

mysqlDoJob.getJobDataMap().put("findMessage", findMessage);

  而后在job的具体方法中,经过getdetail而后getjobdatamap的方式来获取具体的findMessage方法,从而实现定时执行一个方法的操做。

1 public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
2 
3         FindMessage findMessage = (FindMessage) jobExecutionContext.getJobDetail()
4                 .getJobDataMap().get("findMessage");
5         logger.info("get findMessage instance success");
6 
7         findMessage.findDayMessage();
8         logger.info("send ding from mysql is success");
9     }

 

三 Quartz中的JobDataMap使用指南

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

将job加入到scheduler以前,在构建JobDetail时,能够将数据放入JobDataMap。

  因此能够在添加JobDetail时添加jobDataMap,以下:

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

  而后在job的具体类中将其取出,以下:

 1 public class DumbJob implements Job {
 2 
 3     public DumbJob() {
 4     }
 5 
 6     public void execute(JobExecutionContext context)
 7       throws JobExecutionException
 8     {
 9       JobKey key = context.getJobDetail().getKey();
10 
11       JobDataMap dataMap = context.getJobDetail().getJobDataMap();
12 
13       String jobSays = dataMap.getString("jobSays");
14       float myFloatValue = dataMap.getFloat("myFloatValue");
15 
16       System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
17     }
18   }