quartz spring 實現動態定時任務
阿新 • • 發佈:2019-01-30
在實際專案應用中經常會用到定時任務,可以通過quartz和spring的簡單配置即可完成,但如果要改變任務的執行時間、頻率,廢棄任務等就需要改變配置甚至程式碼需要重啟伺服器,這裡介紹一下如何通過quartz與spring的組合實現動態的改變定時任務的狀態的一個實現。
本文章適合對quartz和spring有一定了解的讀者。
spring版本為3.2 quartz版本為2.2.1 如果使用了quartz2.2.1 則spring版本需3.1以上
1.
spring中引入註冊bean
- <bean id="schedulerFactoryBean"
-
class
為什麼要與spring結合?
與spring結合可以使用spring統一管理quartz中任務的生命週期,使得web容器關閉時所有的任務一同關閉。如果不用spring管理可能會出現web容器關閉而任務仍在繼續執行的情況,不與spring結合的話要自己控制任務在容器關閉時一起關閉。
2.建立儲存計劃任務資訊的實體類
Java程式碼- /**
- *
- * @Description: 計劃任務資訊
- * @author snailxr
-
* @date 2014年4月24日 下午10:49:43
- */
- public class ScheduleJob {
- public static final String STATUS_RUNNING = "1";
- public static final String STATUS_NOT_RUNNING = "0";
- public static final String CONCURRENT_IS = "1";
- public static final String CONCURRENT_NOT = "0";
- private Long jobId;
-
private Date createTime;
- private Date updateTime;
- /**
- * 任務名稱
- */
- private String jobName;
- /**
- * 任務分組
- */
- private String jobGroup;
- /**
- * 任務狀態 是否啟動任務
- */
- private String jobStatus;
- /**
- * cron表示式
- */
- private String cronExpression;
- /**
- * 描述
- */
- private String description;
- /**
- * 任務執行時呼叫哪個類的方法 包名+類名
- */
- private String beanClass;
- /**
- * 任務是否有狀態
- */
- private String isConcurrent;
- /**
- * spring bean
- */
- private String springId;
- /**
- * 任務呼叫的方法名
- */
- private String methodName;
- //get set.......
- }
該實體類與資料庫中的表對應,在資料庫中儲存多個計劃任務。
注意:jobName 跟 groupName的組合應該是唯一的,beanClass springId至少有一個
在專案啟動時執行以下程式碼:
Java程式碼- public void init() throws Exception {
- Scheduler scheduler = schedulerFactoryBean.getScheduler();
- // 這裡從資料庫中獲取任務資訊資料
- List<ScheduleJob> jobList = scheduleJobMapper.getAll();
- for (ScheduleJob job : jobList) {
- addJob(job);
- }
- }
- /**
- * 新增任務
- *
- * @param scheduleJob
- * @throws SchedulerException
- */
- public void addJob(ScheduleJob job) throws SchedulerException {
- if (job == null || !ScheduleJob.STATUS_RUNNING.equals(job.getJobStatus())) {
- return;
- }
- Scheduler scheduler = schedulerFactoryBean.getScheduler();
- log.debug(scheduler + ".......................................................................................add");
- TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
- CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
- // 不存在,建立一個
- if (null == trigger) {
- Class clazz = ScheduleJob.CONCURRENT_IS.equals(job.getIsConcurrent()) ? QuartzJobFactory.class : QuartzJobFactoryDisallowConcurrentExecution.class;
- JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();
- jobDetail.getJobDataMap().put("scheduleJob", job);
- CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
- trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(scheduleBuilder).build();
- scheduler.scheduleJob(jobDetail, trigger);
- } else {
- // Trigger已存在,那麼更新相應的定時設定
- CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
- // 按新的cronExpression表示式重新構建trigger
- trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
- // 按新的trigger重新設定job執行
- scheduler.rescheduleJob(triggerKey, trigger);
- }
- }
看到程式碼第20行根據scheduleJob類中CONCURRENT_IS來判斷任務是否有狀態。來給出不同的Job實現類
Java程式碼- /**
- *
- * @Description: 若一個方法一次執行不完下次輪轉時則等待改方法執行完後才執行下一次操作
- * @author snailxr
- * @date 2014年4月24日 下午5:05:47
- */
- @DisallowConcurrentExecution
- public class QuartzJobFactoryDisallowConcurrentExecution implements Job {
- public final Logger log = Logger.getLogger(this.getClass());
- @Override
- public void execute(JobExecutionContext context) throws JobExecutionException {
- ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");
- TaskUtils.invokMethod(scheduleJob);
- }
- }
- /**
- *
- * @Description: 計劃任務執行處 無狀態
- * @author snailxr
- * @date 2014年4月24日 下午5:05:47
- */
- public class QuartzJobFactory implements Job {
- public final Logger log = Logger.getLogger(this.getClass());
- @Override
- public void execute(JobExecutionContext context) throws JobExecutionException {
- ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");
- TaskUtils.invokMethod(scheduleJob);
- }
- }
真正執行計劃任務的程式碼就在TaskUtils.invokMethod(scheduleJob)裡面
通過scheduleJob的beanClass或springId通過反射或spring來獲得需要執行的類,通過methodName來確定執行哪個方法
Java程式碼- public class TaskUtils {
- public final static Logger log = Logger.getLogger(TaskUtils.class);
- /**
- * 通過反射呼叫scheduleJob中定義的方法
- *
- * @param scheduleJob
- */
- public static void invokMethod(ScheduleJob scheduleJob) {
- Object object = null;
- Class clazz = null;
- //springId不為空先按springId查詢bean
- if (StringUtils.isNotBlank(scheduleJob.getSpringId())) {
- object = SpringUtils.getBean(scheduleJob.getSpringId());
- } else if (StringUtils.isNotBlank(scheduleJob.getBeanClass())) {
- try {
- clazz = Class.forName(scheduleJob.getBeanClass());
- object = clazz.newInstance();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- if (object == null) {
- log.error("任務名稱 = [" + scheduleJob.getJobName() + "]---------------未啟動成功,請檢查是否配置正確!!!");
- return;
- }
- clazz = object.getClass();
- Method method = null;
- try {
- method = clazz.getDeclaredMethod(scheduleJob.getMethodName());
- } catch (NoSuchMethodException e) {
- log.error("任務名稱 = [" + scheduleJob.getJobName() + "]---------------未啟動成功,方法名設定錯誤!!!");
- } catch (SecurityException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- if (method != null) {
- try {
- method.invoke(object);
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
對任務的暫停,刪除,修改等操作
Java程式碼- **
- * 獲取所有計劃中的任務列表
- *
- * @return