【原創】Quartz代碼詳解

閱讀目錄

簡單介紹

在實際編程中,咱們常常會遇到定時任務,或是隔一段時間執行指定任務,例如:
1. 在每個月的30號9:00統計人員出勤狀況;
2. 每隔10分鐘執行一次入庫操做;
上面的例子中,須要執行的操做就是Job(做業),而在指定時間或者間隔固定時間去觸發這個Job的就是Trigger(觸發器),而把兩者聯繫在一塊兒的就是Scheduler(調度器);

Quartz主要要理解三個方面:
  1. Scheduler:調度器,將Job和Trigger關聯起來;
  2. Job          :須要執行的做業;
  3. Trigger    :觸發器,指定執行的時間,主要包括兩種方式:
<1>指定時間:如每個月15號9:00,或者天天的12:00等等;
<2>間隔時間:如每隔10分鐘執行一次,每隔2h執行一次等等;

1、Quartz簡單實例

下面給出一個簡單的示例:
      
      
      
      
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
 
  Scheduler sched = schedFact.getScheduler();
 
  sched.start();
 
  // define the job and tie it to our HelloJob class
  JobDetail job = newJob(HelloJob.class)
      .withIdentity("myJob", "group1")
      .build();
 
  // Trigger the job to run now, and then every 40 seconds
  trigger = newTrigger()
      .withIdentity("myTrigger", "group1")
      .startNow()
      .withSchedule(simpleSchedule()
          .withIntervalInSeconds(40)
          .repeatForever())
      .build();
 
  // Tell quartz to schedule the job using our trigger
  sched.scheduleJob(job, trigger);
下面的截圖是Java源碼給出的示例:


咱們在使用Quartz時,通常的操做步驟爲:
步驟1:自定義Job類(如:MyJob),實現Job接口;
步驟2:使用 JobBuilder生成JobDetail;
步驟3:定義Trigger類,通常使用TriggerBuilder生成;
步驟4:定義Scheduler類,使用 Scheduler . scheduleJob(job, trigger)將job和trigger進行關聯;
步驟5Scheduler.start();
步驟6:當須要關閉 Scheduler 時,使用 Scheduler. shutdown();
下面將具體進行講解。

2、Job、JobDetail、JobBuilder

官方定義:
  1. Job - an interface to be implemented by components that you wish to have executed by the scheduler.
  2. JobDetail - used to define instances of Jobs.
  3. JobBuilder - used to define/build JobDetail instances, which define instances of Jobs.

Job

Job是一個接口類,下面是它的源碼:

若是要實現本身的做業(任務),最簡單的方式就是實現該接口,並實現接口中的execute()方法,當觸發該做業時,就是執行execute()方法;
public class HelloJob implements Job {

    public HelloJob() {
    }

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

JobDetail 

JobDetail :用於定義Job的實例,通常該類都是經過JobBuilder生成;
JobBuilder: 用於定義或建立JobDetail實例,使用了建造者模式;
JobDetail job = newJob(HelloJob.class)
      .withIdentity("myJob", "group1") // name "myJob", group "group1"
      .build();

     
     
     
     
//帶參數
JobDetail job = JobBuilder.newJob(clazz)
                    .withIdentity(new JobKey(jobName, groupName))
                    .usingJobData(new JobDataMap(params))
                    .build();
從上面的接口類中咱們能夠知道,只要有了JobDetail的實現類,咱們就能夠獲取到:
  1. key;
  2. JobClass;
  3. JobDataMap;等
那咱們在自定義的Job中怎麼才能獲取到JobDetail相關信息呢?

由上面可知,當自定義的做業被觸發時,咱們能夠經過JobExecutionContext獲取到JobDetail,進而獲取JobDataMap、JobKey、JobClass等等信息;


JobBuilder

這裏JobBuilder使用了建造者模式,下面對其源碼進行解析;
對於建造者模式,這裏不進行詳述,首先看下它的字段:


JobKey:做業的惟一標識,由name和group組成,以下:


jobClass:指定上述咱們講的自定義的Job(如:HelloJob );




JobDataMap:能夠經過這個屬性給自定義的Job(如:HelloJob)傳遞參數;

由於使用了建造者模式,因此實際建立的對象爲:JobDetailImpl

JobDetailImpl類實現了JobDeteil接口,以下:
下圖是類的層級結構圖:



3、Trigger、TriggerBuilder

官方定義:
Trigger - a component that defines the schedule upon which a given Job will be executed.
TriggerBuilder - used to define/build Trigger instances.

下面是觸發器層次結構,經常使用的有SimpleTrigger、CronTrigger等;


SimpleTrigger



主要屬性:


CronTrigger

使用CronTrigger能夠進行很強大的控制,關於Cron表達式,下面的章節會介紹;

主要的參數:



TriggerBuilder

首先看下實現的例子:使用的也是建造者模式(建造者模式可自行搜索);

TriggerBuilder的屬性以下:


經過withIdentity()方法設定trigger惟一標識:


經過forJob()方法將Trigger和指定的Job綁定
前面咱們已經知道:
  • 在使用JobBuilder建立JobDetail時,經過方法withIdentity()指定了JobDetail的JobKey
  • 這裏經過TriggerBuilder的forJob()一樣指定了JobKey;
==>只要上面兩個JobKey設置的相同,則JobDetail和Trigger便綁定在一塊兒了;
後面只須要使用Scheduler.scheduleJob(job,trigger)進行調度便可;


經過方法withSchedule設置調度器:

從上面能夠看出,這個方法傳遞進去的其實是一個調度器Scheduler的Builder,真正的調度器Scheduler仍是須要經過ScheduleBuilder去建立,這裏使用的仍是建造者模式,從下面的層次能夠看出,已經實現了4種ScheduleBuilder,這裏以SimplesCheduleBuilder進行分析,其餘相似

下面咱們看看SimplesCheduleBuilder的源碼實現;



上面是build方法,能夠看出,真正建立的是:SimpleTriggerImpl類,這個類在上面已經介紹過,它是Trigger的一個實現類;



咱們接着看TriggerBuilder的build方法:



4、Scheduler

官方定義:
Scheduler - the main API for interacting with the scheduler.
與調度程序交互的主要API。

具體實現類:

主要的方法:
      
      
      
      
Scheduler  scheduler = StdSchedulerFactory.getDefaultScheduler();scheduler.schedeleJob(Job,trigger)scheduler.start();scheduler.shutdown();
說明:
只有在scheduler.start();以後,trigger以及Job纔能有效運行;
shutdown用於關閉;

schedeleJob方法介紹
    
    
    
    
// Tell quartz to schedule the job using our trigger
  sched.scheduleJob(job, trigger);
官方介紹以下:

如今以StdScheduler做爲示例進行介紹:

start()方法介紹

使用start()方法來激活Trigger,執行定時任務;




5、JobListener、SchedulerListener、TriggerListener




對監聽進行註冊:

      

6、Cron表達式

語法格式:
1.png
示例:
2.png
3.png
  • * ("all values") - used to select all values within a field. For example, "" in the minute field means *"every minute".java

  • ? ("no specific value") - useful when you need to specify something in one of the two fields in which the character is allowed, but not the other. For example, if I want my trigger to fire on a particular day of the month (say, the 10th), but don't care what day of the week that happens to be, I would put "10" in the day-of-month field, and "?" in the day-of-week field. See the examples below for clarification.express

  • - - used to specify ranges. For example, "10-12" in the hour field means "the hours 10, 11 and 12".編程

  • , - used to specify additional values. For example, "MON,WED,FRI" in the day-of-week field means "the days Monday, Wednesday, and Friday".windows

  • / - used to specify increments. For example, "0/15" in the seconds field means "the seconds 0, 15, 30, and 45". And "5/15" in the seconds field means "the seconds 5, 20, 35, and 50". You can also specify '/' after the '' character - in this case '' is equivalent to having '0' before the '/'. '1/3' in the day-of-month field means "fire every 3 days starting on the first day of the month".app

  • L ("last") - has different meaning in each of the two fields in which it is allowed. For example, the value "L" in the day-of-month field means "the last day of the month" - day 31 for January, day 28 for February on non-leap years. If used in the day-of-week field by itself, it simply means "7" or "SAT". But if used in the day-of-week field after another value, it means "the last xxx day of the month" - for example "6L" means "the last friday of the month". You can also specify an offset from the last day of the month, such as "L-3" which would mean the third-to-last day of the calendar month. When using the 'L' option, it is important not to specify lists, or ranges of values, as you'll get confusing/unexpected results.ide

  • W ("weekday") - used to specify the weekday (Monday-Friday) nearest the given day. As an example, if you were to specify "15W" as the value for the day-of-month field, the meaning is: "the nearest weekday to the 15th of the month". So if the 15th is a Saturday, the trigger will fire on Friday the 14th. If the 15th is a Sunday, the trigger will fire on Monday the 16th. If the 15th is a Tuesday, then it will fire on Tuesday the 15th. However if you specify "1W" as the value for day-of-month, and the 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not 'jump' over the boundary of a month's days. The 'W' character can only be specified when the day-of-month is a single day, not a range or list of days.ui

The 'L' and 'W' characters can also be combined in the day-of-month field to yield 'LW', which translates to *"last weekday of the month"*.
  • # - used to specify "the nth" XXX day of the month. For example, the value of "6#3" in the day-of-week field means"the third Friday of the month" (day 6 = Friday and "#3" = the 3rd one in the month). Other examples: "2#1" = the first Monday of the month and "4#5" = the fifth Wednesday of the month. Note that if you specify "#5" and there is not 5 of the given day-of-week in the month, then no firing will occur that month.
                                                                                                                                                                                                                                                                                  回到頂部

7、程序示例


AbstractSchedule類:抽象基類



      
      
      
      
package com.sssppp.TimerSchedule.quartz.schedule;import java.util.Date;import java.util.Map;import java.util.Properties;import org.quartz.CronScheduleBuilder;import org.quartz.CronTrigger;import org.quartz.JobBuilder;import org.quartz.JobDataMap;import org.quartz.JobDetail;import org.quartz.JobKey;import org.quartz.Scheduler;import org.quartz.SchedulerException;import org.quartz.TriggerBuilder;import org.quartz.impl.StdSchedulerFactory;import com.sssppp.TimerSchedule.quartz.listeners.MyJobListener;import com.sssppp.TimerSchedule.quartz.listeners.MySchedulerListener;import com.sssppp.TimerSchedule.quartz.listeners.MyTriggerListener;public abstract class AbstractSchedule { public Scheduler scheduler = null; private static final String JOB_GROUPNAME = "MY_JOB_GROUP"; private static final String TRIGGER_GROUPNAME = "MY_TRIGGER_GROUP"; /** * 初始化Scheduler,並添加Listeners */ public AbstractSchedule() { try { if (scheduler == null) { System.out.println("Begin init scheduler..."); try { Properties props = new Properties(); props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, "org.quartz.simpl.SimpleThreadPool"); props.put("org.quartz.threadPool.threadCount", "100");// 同時100個線程運行 props.put("org.quartz.jobStore.misfireThreshold", "180000");// trigger過時30分鐘內還有效 StdSchedulerFactory factory = new StdSchedulerFactory(); factory.initialize(props); scheduler = factory.getScheduler(); } catch (SchedulerException e) { e.printStackTrace(); } scheduler.getListenerManager().addJobListener(new MyJobListener()); scheduler.getListenerManager().addSchedulerListener( new MySchedulerListener()); scheduler.getListenerManager().addTriggerListener( new MyTriggerListener()); } } catch (Exception e) { System.err.println("Init scheduler failed, error message :" + e); } } public abstract Scheduler handleJob(String jobName, String triggerName, String cronStr); @SuppressWarnings({ "unchecked", "rawtypes" }) public void scheduleJob(String jobName, String triggerName, String express, Class clazz) { JobDetail job = null; CronTrigger trigger = null; try { job = JobBuilder.newJob(clazz).withIdentity(jobName, JOB_GROUPNAME) .build(); trigger = TriggerBuilder.newTrigger() .withIdentity(triggerName, TRIGGER_GROUPNAME) .forJob(jobName, JOB_GROUPNAME) .withSchedule(CronScheduleBuilder.cronSchedule(express)) .build(); } catch (Exception e) { System.err.println("scheduler ParseException!" + e); } Date date = null; try { date = scheduler.scheduleJob(job, trigger); } catch (SchedulerException e) { System.err.println("scheduler SchedulerException!" + e); } System.out.println(job.getKey().toString() + " has been scheduled to run at: " + date + " and repeat based on expression: " + trigger.getCronExpression()); } /** * 建立JobTrigger,並使用schedulerjobTrigger進行關聯 * * @param jobName * @param triggerName * @param express * :cronStr表達式 * @param clazz * jobclass * @param params * Job使用的參數 */ @SuppressWarnings({ "unchecked", "rawtypes" }) public void scheduleJobWithParams(String jobName, String triggerName, String express, Class clazz, Map<String, Object> params) { JobDetail job = null; CronTrigger trigger = null; try { job = JobBuilder.newJob(clazz) .withIdentity(new JobKey(jobName, JOB_GROUPNAME)) .usingJobData(new JobDataMap(params)).build(); trigger = TriggerBuilder.newTrigger() .withIdentity(triggerName, TRIGGER_GROUPNAME) .forJob(jobName, JOB_GROUPNAME) .withSchedule(CronScheduleBuilder.cronSchedule(express)) .build(); } catch (Exception e) { System.err.println("scheduler ParseException!" + e); } Date date = null; try { date = scheduler.scheduleJob(job, trigger); } catch (SchedulerException e) { System.err.println("scheduler SchedulerException!" + e); } System.out.println(job.getKey().toString() + " has been scheduled to run at: " + date + " and repeat based on expression: " + trigger.getCronExpression()); } /** * Starts the Scheduler's threads that fire Triggers */ public void startJob() { try { this.scheduler.start(); } catch (SchedulerException e) { System.err.println("trigger job error!" + e); } } public boolean stopJob(String jobName) { boolean b = false; JobKey jobkey = new JobKey(jobName, JOB_GROUPNAME); try { if (this.scheduler.checkExists(jobkey)) { b = this.scheduler.deleteJob(jobkey); System.out.println("Stop Job[" + jobName + "] success."); } } catch (SchedulerException e) { System.err.println("Stop job fail."); e.printStackTrace(); } return b; } public void shutdownScheduler() { try { this.scheduler.shutdown(true); System.out.println("Shutdown scheduler success."); } catch (SchedulerException e) { System.err.println("Shutdown Scheduler fail."); e.printStackTrace(); } }}

MyJobSchedule.java
     
     
     
     
package com.sssppp.TimerSchedule.quartz.schedule;import java.util.HashMap;import java.util.Map;import org.quartz.Scheduler;import com.sssppp.TimerSchedule.quartz.Jobs.MyJob;public class MyJobSchedule extends AbstractSchedule { private static MyJobSchedule myJobSchedule = new MyJobSchedule(); private MyJobSchedule() { } public static MyJobSchedule getInstance() { return myJobSchedule; } @Override public Scheduler handleJob(String jobName, String triggerName, String cronStr) { Map<String, Object> params = new HashMap<String, Object>(); params.put(MyJob.JOB_PARAM_KEY, "This is myJob param"); scheduleJobWithParams(jobName, triggerName, cronStr, MyJob.class, params); startJob(); return this.scheduler; }}

MyJob.java

      
      
      
      
package com.sssppp.TimerSchedule.quartz.Jobs;import java.util.Date;import org.quartz.Job;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;import com.ibm.icu.text.SimpleDateFormat;public class MyJob implements Job { public final static String JOB_PARAM_KEY = "jobParam"; @Override public void execute(JobExecutionContext jobexecutioncontext) throws JobExecutionException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 獲取傳遞給Job的參數 String param = (String) jobexecutioncontext.getJobDetail() .getJobDataMap().get(JOB_PARAM_KEY); System.out.println("Now exec MyJob,param【" + param + "】, time:" + sdf.format(new Date())); }}

QuartzManager.java
     
     
     
     
package com.sssppp.TimerSchedule.quartz;import com.sssppp.TimerSchedule.quartz.schedule.MyJobSchedule;public class QuartzManager { private static final QuartzManager quartzManager = new QuartzManager(); private static final String MY_JOB_NAME = "MY_JOB_NAME"; private static final String MY_TRIGGER_NAME = "MY_TRIGGER_NAME"; private static final String MY_JOB_CRONSTR = "0/5 * * * * ?"; private QuartzManager() { } public static QuartzManager getInstance() { return quartzManager; } public void startMyJob() { MyJobSchedule.getInstance().handleJob(MY_JOB_NAME, MY_TRIGGER_NAME, MY_JOB_CRONSTR); } public void stopMyJobAndShutdownScheduler() { MyJobSchedule.getInstance().stopJob(MY_JOB_NAME); MyJobSchedule.getInstance().shutdownScheduler(); }}





TestCase.java

     
     
     
     
package com.sssppp.TimerSchedule.quartz;public class TestCase { @SuppressWarnings("static-access") public static void main(String[] args) throws InterruptedException { QuartzManager.getInstance().startMyJob(); Thread.currentThread().sleep(12 * 1000); QuartzManager.getInstance().stopMyJobAndShutdownScheduler(); }}



























相關文章
相關標籤/搜索