Quartz框架来实现定时任务

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

一 Quartz简单使用

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

  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)方法。

注意: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方法中。如下:  

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 }

  以上需要注意几点:

  1. ApplicationListener接口中需要传入监听参数(ApplicationReadyEvent),因为如果不传入参数的话,会对每个event都进行监听,则会发生同时执行好几个定时任务这样的惨状。这个问题不只存在与定时任务的监听。

  2. 先看一段解释:

  我们传给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   }

 

posted @ 2019-07-19 18:22  Mask_D  阅读(1835)  评论(0编辑  收藏  举报