1. 程式人生 > >quartz spring 實現動態定時任務

quartz spring 實現動態定時任務

在實際專案應用中經常會用到定時任務,可以通過quartz和spring的簡單配置即可完成,但如果要改變任務的執行時間、頻率,廢棄任務等就需要改變配置甚至程式碼需要重啟伺服器,這裡介紹一下如何通過quartz與spring的組合實現動態的改變定時任務的狀態的一個實現。

本文章適合對quartz和spring有一定了解的讀者。
spring版本為3.2  quartz版本為2.2.1  如果使用了quartz2.2.1 則spring版本需3.1以上

1.
spring中引入註冊bean

Xml程式碼  收藏程式碼
  1. <bean id="schedulerFactoryBean"  
  2.         class
    ="org.springframework.scheduling.quartz.SchedulerFactoryBean" />  

為什麼要與spring結合?
與spring結合可以使用spring統一管理quartz中任務的生命週期,使得web容器關閉時所有的任務一同關閉。如果不用spring管理可能會出現web容器關閉而任務仍在繼續執行的情況,不與spring結合的話要自己控制任務在容器關閉時一起關閉。

2.建立儲存計劃任務資訊的實體類

Java程式碼  收藏程式碼
  1. /** 
  2.  *  
  3. * @Description: 計劃任務資訊 
  4. * @author snailxr 
  5. * @date 2014年4月24日 下午10:49:43
     
  6.  */  
  7. public class ScheduleJob {  
  8.     public static final String STATUS_RUNNING = "1";  
  9.     public static final String STATUS_NOT_RUNNING = "0";  
  10.     public static final String CONCURRENT_IS = "1";  
  11.     public static final String CONCURRENT_NOT = "0";  
  12.     private Long jobId;  
  13.     private Date createTime;  
  14.     private Date updateTime;  
  15.     /** 
  16.      * 任務名稱 
  17.      */  
  18.     private String jobName;  
  19.     /** 
  20.      * 任務分組 
  21.      */  
  22.     private String jobGroup;  
  23.     /** 
  24.      * 任務狀態 是否啟動任務 
  25.      */  
  26.     private String jobStatus;  
  27.     /** 
  28.      * cron表示式 
  29.      */  
  30.     private String cronExpression;  
  31.     /** 
  32.      * 描述 
  33.      */  
  34.     private String description;  
  35.     /** 
  36.      * 任務執行時呼叫哪個類的方法 包名+類名 
  37.      */  
  38.     private String beanClass;  
  39.     /** 
  40.      * 任務是否有狀態 
  41.      */  
  42.     private String isConcurrent;  
  43.     /** 
  44.      * spring bean 
  45.      */  
  46.     private String springId;  
  47.     /** 
  48.      * 任務呼叫的方法名 
  49.      */  
  50.     private String methodName;  
  51.     //get  set.......  
  52. }  

 該實體類與資料庫中的表對應,在資料庫中儲存多個計劃任務。

  注意:jobName 跟 groupName的組合應該是唯一的,beanClass springId至少有一個

在專案啟動時執行以下程式碼:

Java程式碼  收藏程式碼
  1. public void init() throws Exception {  
  2.         Scheduler scheduler = schedulerFactoryBean.getScheduler();  
  3.         // 這裡從資料庫中獲取任務資訊資料  
  4.         List<ScheduleJob> jobList = scheduleJobMapper.getAll();  
  5.         for (ScheduleJob job : jobList) {  
  6.             addJob(job);  
  7.         }  
  8.     }  
Java程式碼  收藏程式碼
  1. /** 
  2.      * 新增任務 
  3.      *  
  4.      * @param scheduleJob 
  5.      * @throws SchedulerException 
  6.      */  
  7.     public void addJob(ScheduleJob job) throws SchedulerException {  
  8.         if (job == null || !ScheduleJob.STATUS_RUNNING.equals(job.getJobStatus())) {  
  9.             return;  
  10.         }  
  11.         Scheduler scheduler = schedulerFactoryBean.getScheduler();  
  12.         log.debug(scheduler + ".......................................................................................add");  
  13.         TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());  
  14.         CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);  
  15.         // 不存在,建立一個  
  16.         if (null == trigger) {  
  17.             Class clazz = ScheduleJob.CONCURRENT_IS.equals(job.getIsConcurrent()) ? QuartzJobFactory.class : QuartzJobFactoryDisallowConcurrentExecution.class;  
  18.             JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();  
  19.             jobDetail.getJobDataMap().put("scheduleJob", job);  
  20.             CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());  
  21.             trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(scheduleBuilder).build();  
  22.             scheduler.scheduleJob(jobDetail, trigger);  
  23.         } else {  
  24.             // Trigger已存在,那麼更新相應的定時設定  
  25.             CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());  
  26.             // 按新的cronExpression表示式重新構建trigger  
  27.             trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();  
  28.             // 按新的trigger重新設定job執行  
  29.             scheduler.rescheduleJob(triggerKey, trigger);  
  30.         }  
  31.     }  

 看到程式碼第20行根據scheduleJob類中CONCURRENT_IS來判斷任務是否有狀態。來給出不同的Job實現類

Java程式碼  收藏程式碼
  1. /** 
  2.  *  
  3.  * @Description: 若一個方法一次執行不完下次輪轉時則等待改方法執行完後才執行下一次操作 
  4.  * @author snailxr 
  5.  * @date 2014年4月24日 下午5:05:47 
  6.  */  
  7. @DisallowConcurrentExecution  
  8. public class QuartzJobFactoryDisallowConcurrentExecution implements Job {  
  9.     public final Logger log = Logger.getLogger(this.getClass());  
  10.     @Override  
  11.     public void execute(JobExecutionContext context) throws JobExecutionException {  
  12.         ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");  
  13.         TaskUtils.invokMethod(scheduleJob);  
  14.     }  
  15. }  
Java程式碼  收藏程式碼
  1. /** 
  2.  *  
  3.  * @Description: 計劃任務執行處 無狀態 
  4.  * @author snailxr 
  5.  * @date 2014年4月24日 下午5:05:47 
  6.  */  
  7. public class QuartzJobFactory implements Job {  
  8.     public final Logger log = Logger.getLogger(this.getClass());  
  9.     @Override  
  10.     public void execute(JobExecutionContext context) throws JobExecutionException {  
  11.         ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");  
  12.         TaskUtils.invokMethod(scheduleJob);  
  13.     }  
  14. }  

真正執行計劃任務的程式碼就在TaskUtils.invokMethod(scheduleJob)裡面

通過scheduleJob的beanClass或springId通過反射或spring來獲得需要執行的類,通過methodName來確定執行哪個方法

Java程式碼  收藏程式碼
  1. public class TaskUtils {  
  2.     public final static Logger log = Logger.getLogger(TaskUtils.class);  
  3.     /** 
  4.      * 通過反射呼叫scheduleJob中定義的方法 
  5.      *  
  6.      * @param scheduleJob 
  7.      */  
  8.     public static void invokMethod(ScheduleJob scheduleJob) {  
  9.         Object object = null;  
  10.         Class clazz = null;  
  11.                 //springId不為空先按springId查詢bean  
  12.         if (StringUtils.isNotBlank(scheduleJob.getSpringId())) {  
  13.             object = SpringUtils.getBean(scheduleJob.getSpringId());  
  14.         } else if (StringUtils.isNotBlank(scheduleJob.getBeanClass())) {  
  15.             try {  
  16.                 clazz = Class.forName(scheduleJob.getBeanClass());  
  17.                 object = clazz.newInstance();  
  18.             } catch (Exception e) {  
  19.                 // TODO Auto-generated catch block  
  20.                 e.printStackTrace();  
  21.             }  
  22.         }  
  23.         if (object == null) {  
  24.             log.error("任務名稱 = [" + scheduleJob.getJobName() + "]---------------未啟動成功,請檢查是否配置正確!!!");  
  25.             return;  
  26.         }  
  27.         clazz = object.getClass();  
  28.         Method method = null;  
  29.         try {  
  30.             method = clazz.getDeclaredMethod(scheduleJob.getMethodName());  
  31.         } catch (NoSuchMethodException e) {  
  32.             log.error("任務名稱 = [" + scheduleJob.getJobName() + "]---------------未啟動成功,方法名設定錯誤!!!");  
  33.         } catch (SecurityException e) {  
  34.             // TODO Auto-generated catch block  
  35.             e.printStackTrace();  
  36.         }  
  37.         if (method != null) {  
  38.             try {  
  39.                 method.invoke(object);  
  40.             } catch (IllegalAccessException e) {  
  41.                 // TODO Auto-generated catch block  
  42.                 e.printStackTrace();  
  43.             } catch (IllegalArgumentException e) {  
  44.                 // TODO Auto-generated catch block  
  45.                 e.printStackTrace();  
  46.             } catch (InvocationTargetException e) {  
  47.                 // TODO Auto-generated catch block  
  48.                 e.printStackTrace();  
  49.             }  
  50.         }  
  51.     }  
  52. }  

 對任務的暫停,刪除,修改等操作

Java程式碼  收藏程式碼
  1. **  
  2.      * 獲取所有計劃中的任務列表  
  3.      *   
  4.      * @return