• 【原创】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执行一次等等;

    一、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();
    下面将具体进行讲解。

    二、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接口,如下:
    下图是类的层级结构图:



    三、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方法:



    四、Scheduler

    官方定义:
    Scheduler - the main API for interacting with the scheduler.
    与调度程序交互的主要API。

    具体实现类:

    主要的方法:
    1. Scheduler  scheduler = StdSchedulerFactory.getDefaultScheduler();
    2. scheduler.schedeleJob(Job,trigger)
    3. scheduler.start();
    4. 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,执行定时任务;




    五、JobListener、SchedulerListener、TriggerListener




    对监听进行注册:

          

    六、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".

    • ? ("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.

    • - - 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".

    • / - 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".

    • 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.

    • 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.

    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.
                                                                                                                                                                                                                              回到顶部

    七、程序示例


    AbstractSchedule类:抽象基类



    1. package com.sssppp.TimerSchedule.quartz.schedule;
    2. import java.util.Date;
    3. import java.util.Map;
    4. import java.util.Properties;
    5. import org.quartz.CronScheduleBuilder;
    6. import org.quartz.CronTrigger;
    7. import org.quartz.JobBuilder;
    8. import org.quartz.JobDataMap;
    9. import org.quartz.JobDetail;
    10. import org.quartz.JobKey;
    11. import org.quartz.Scheduler;
    12. import org.quartz.SchedulerException;
    13. import org.quartz.TriggerBuilder;
    14. import org.quartz.impl.StdSchedulerFactory;
    15. import com.sssppp.TimerSchedule.quartz.listeners.MyJobListener;
    16. import com.sssppp.TimerSchedule.quartz.listeners.MySchedulerListener;
    17. import com.sssppp.TimerSchedule.quartz.listeners.MyTriggerListener;
    18. public abstract class AbstractSchedule {
    19. public Scheduler scheduler = null;
    20. private static final String JOB_GROUPNAME = "MY_JOB_GROUP";
    21. private static final String TRIGGER_GROUPNAME = "MY_TRIGGER_GROUP";
    22. /**
    23. * 初始化Scheduler,并添加Listeners
    24. */
    25. public AbstractSchedule() {
    26. try {
    27. if (scheduler == null) {
    28. System.out.println("Begin init scheduler...");
    29. try {
    30. Properties props = new Properties();
    31. props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS,
    32. "org.quartz.simpl.SimpleThreadPool");
    33. props.put("org.quartz.threadPool.threadCount", "100");// 同时100个线程运行
    34. props.put("org.quartz.jobStore.misfireThreshold", "180000");// trigger过期30分钟内还有效
    35. StdSchedulerFactory factory = new StdSchedulerFactory();
    36. factory.initialize(props);
    37. scheduler = factory.getScheduler();
    38. } catch (SchedulerException e) {
    39. e.printStackTrace();
    40. }
    41. scheduler.getListenerManager().addJobListener(new MyJobListener());
    42. scheduler.getListenerManager().addSchedulerListener(
    43. new MySchedulerListener());
    44. scheduler.getListenerManager().addTriggerListener(
    45. new MyTriggerListener());
    46. }
    47. } catch (Exception e) {
    48. System.err.println("Init scheduler failed, error message :" + e);
    49. }
    50. }
    51. public abstract Scheduler handleJob(String jobName, String triggerName,
    52. String cronStr);
    53. @SuppressWarnings({ "unchecked", "rawtypes" })
    54. public void scheduleJob(String jobName, String triggerName, String express,
    55. Class clazz) {
    56. JobDetail job = null;
    57. CronTrigger trigger = null;
    58. try {
    59. job = JobBuilder.newJob(clazz).withIdentity(jobName, JOB_GROUPNAME)
    60. .build();
    61. trigger = TriggerBuilder.newTrigger()
    62. .withIdentity(triggerName, TRIGGER_GROUPNAME)
    63. .forJob(jobName, JOB_GROUPNAME)
    64. .withSchedule(CronScheduleBuilder.cronSchedule(express))
    65. .build();
    66. } catch (Exception e) {
    67. System.err.println("scheduler ParseException!" + e);
    68. }
    69. Date date = null;
    70. try {
    71. date = scheduler.scheduleJob(job, trigger);
    72. } catch (SchedulerException e) {
    73. System.err.println("scheduler SchedulerException!" + e);
    74. }
    75. System.out.println(job.getKey().toString()
    76. + " has been scheduled to run at: " + date
    77. + " and repeat based on expression: "
    78. + trigger.getCronExpression());
    79. }
    80. /**
    81. * 创建JobTrigger,并使用schedulerjobTrigger进行关联
    82. *
    83. * @param jobName
    84. * @param triggerName
    85. * @param express
    86. * :cronStr表达式
    87. * @param clazz
    88. * jobclass
    89. * @param params
    90. * Job使用的参数
    91. */
    92. @SuppressWarnings({ "unchecked", "rawtypes" })
    93. public void scheduleJobWithParams(String jobName, String triggerName,
    94. String express, Class clazz, Map<String, Object> params) {
    95. JobDetail job = null;
    96. CronTrigger trigger = null;
    97. try {
    98. job = JobBuilder.newJob(clazz)
    99. .withIdentity(new JobKey(jobName, JOB_GROUPNAME))
    100. .usingJobData(new JobDataMap(params)).build();
    101. trigger = TriggerBuilder.newTrigger()
    102. .withIdentity(triggerName, TRIGGER_GROUPNAME)
    103. .forJob(jobName, JOB_GROUPNAME)
    104. .withSchedule(CronScheduleBuilder.cronSchedule(express))
    105. .build();
    106. } catch (Exception e) {
    107. System.err.println("scheduler ParseException!" + e);
    108. }
    109. Date date = null;
    110. try {
    111. date = scheduler.scheduleJob(job, trigger);
    112. } catch (SchedulerException e) {
    113. System.err.println("scheduler SchedulerException!" + e);
    114. }
    115. System.out.println(job.getKey().toString()
    116. + " has been scheduled to run at: " + date
    117. + " and repeat based on expression: "
    118. + trigger.getCronExpression());
    119. }
    120. /**
    121. * Starts the Scheduler's threads that fire Triggers
    122. */
    123. public void startJob() {
    124. try {
    125. this.scheduler.start();
    126. } catch (SchedulerException e) {
    127. System.err.println("trigger job error!" + e);
    128. }
    129. }
    130. public boolean stopJob(String jobName) {
    131. boolean b = false;
    132. JobKey jobkey = new JobKey(jobName, JOB_GROUPNAME);
    133. try {
    134. if (this.scheduler.checkExists(jobkey)) {
    135. b = this.scheduler.deleteJob(jobkey);
    136. System.out.println("Stop Job[" + jobName + "] success.");
    137. }
    138. } catch (SchedulerException e) {
    139. System.err.println("Stop job fail.");
    140. e.printStackTrace();
    141. }
    142. return b;
    143. }
    144. public void shutdownScheduler() {
    145. try {
    146. this.scheduler.shutdown(true);
    147. System.out.println("Shutdown scheduler success.");
    148. } catch (SchedulerException e) {
    149. System.err.println("Shutdown Scheduler fail.");
    150. e.printStackTrace();
    151. }
    152. }
    153. }

    MyJobSchedule.java
    1. package com.sssppp.TimerSchedule.quartz.schedule;
    2. import java.util.HashMap;
    3. import java.util.Map;
    4. import org.quartz.Scheduler;
    5. import com.sssppp.TimerSchedule.quartz.Jobs.MyJob;
    6. public class MyJobSchedule extends AbstractSchedule {
    7. private static MyJobSchedule myJobSchedule = new MyJobSchedule();
    8. private MyJobSchedule() {
    9. }
    10. public static MyJobSchedule getInstance() {
    11. return myJobSchedule;
    12. }
    13. @Override
    14. public Scheduler handleJob(String jobName, String triggerName,
    15. String cronStr) {
    16. Map<String, Object> params = new HashMap<String, Object>();
    17. params.put(MyJob.JOB_PARAM_KEY, "This is myJob param");
    18. scheduleJobWithParams(jobName, triggerName, cronStr, MyJob.class, params);
    19. startJob();
    20. return this.scheduler;
    21. }
    22. }

    MyJob.java

    1. package com.sssppp.TimerSchedule.quartz.Jobs;
    2. import java.util.Date;
    3. import org.quartz.Job;
    4. import org.quartz.JobExecutionContext;
    5. import org.quartz.JobExecutionException;
    6. import com.ibm.icu.text.SimpleDateFormat;
    7. public class MyJob implements Job {
    8. public final static String JOB_PARAM_KEY = "jobParam";
    9. @Override
    10. public void execute(JobExecutionContext jobexecutioncontext)
    11. throws JobExecutionException {
    12. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    13. // 获取传递给Job的参数
    14. String param = (String) jobexecutioncontext.getJobDetail()
    15. .getJobDataMap().get(JOB_PARAM_KEY);
    16. System.out.println("Now exec MyJob,param【" + param + "】, time:"
    17. + sdf.format(new Date()));
    18. }
    19. }

    QuartzManager.java
    1. package com.sssppp.TimerSchedule.quartz;
    2. import com.sssppp.TimerSchedule.quartz.schedule.MyJobSchedule;
    3. public class QuartzManager {
    4. private static final QuartzManager quartzManager = new QuartzManager();
    5. private static final String MY_JOB_NAME = "MY_JOB_NAME";
    6. private static final String MY_TRIGGER_NAME = "MY_TRIGGER_NAME";
    7. private static final String MY_JOB_CRONSTR = "0/5 * * * * ?";
    8. private QuartzManager() {
    9. }
    10. public static QuartzManager getInstance() {
    11. return quartzManager;
    12. }
    13. public void startMyJob() {
    14. MyJobSchedule.getInstance().handleJob(MY_JOB_NAME, MY_TRIGGER_NAME,
    15. MY_JOB_CRONSTR);
    16. }
    17. public void stopMyJobAndShutdownScheduler() {
    18. MyJobSchedule.getInstance().stopJob(MY_JOB_NAME);
    19. MyJobSchedule.getInstance().shutdownScheduler();
    20. }
    21. }





    TestCase.java

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



























  • 相关阅读:
    shell命令--stat
    英文段子
    OCP读书笔记(16)
    shell命令--uptime
    OCP读书笔记(15)
    shell命令--dmesg
    OCP读书笔记(14)
    shell命令--hostname
    OCP读书笔记(13)
    shell命令--uname
  • 原文地址:https://www.cnblogs.com/ssslinppp/p/5055524.html
Copyright © 2020-2023  润新知