Spring 和Quartz2 整合實現動態定時任務
問題起始:
最近要做一個定時任務,使用Spring的定時任務配置也可以實現。但是很多時候,我們常常會遇到需要動態的添加或修改任務,而spring中所提供的定時任務組件卻只能夠通過修改xml中trigger的配置才能控制定時任務的時間以及任務的啟用或停止,這在帶給我們方便的同時也失去了動態配置任務的靈活性。我搜索了一些網上的解決方法,都沒有很好的解決這個問題,而且大多數提到的解決方案都停留在Quartz 1.x系列版本上,所用到的代碼和API已經不能適用于新版本的Spring和Quartz。那么讓我們來解決它吧、
知識點補充:
1、quartz任務調度快速入門:
該資料使用 的是舊版本的quarzt,里面的實例化 jobdetail 和 Trigger的方式都不適用了。但是,對很多基礎的概念解釋的相當清晰。推薦只看概念,加深理解任務調度的工作機制。
2、帶參數執行 任務調度
在job中,不可能不需要參數,這時候參數的傳遞就顯得尤為重要了。quartz2提供參數傳遞方法是:
1).jobDetail.getJobDataMap().put("timerconfig", timerConfig); 將 timerConfig 以map集合的形式傳遞給 任務執行時的上下文。
2). (TimerConfig) context.getJobDetail().getJobDataMap().get("timerconfig"); context是job接口中execute(JobExecutionContext context);將剛剛傳遞的timerconfig取出
如果timerconfig是對象,則用get();其他的則使用對應的get方法即可。
3、如何實例化JobDetail
在quartz2中,實例化任務的方式變化較大,是使用builder進行實例化。 JobDetail jobDetail = newJob(MyTask.class) .withIdentity(name, Groupname).build();
4、如何實例化Trigger
Trigger trigger = newTrigger()
.withIdentity(name, Groupname)
.startNow()
.withSchedule(
CronScheduleBuilder
.cronSchedule(new CronExpression(
expression))).build();
此版本不再采用1版本的 SimpleTrigger /cronTrigger.而是在使用調度器(schedule)的時候選擇是用 SimpleScheduleBuilder還是 CronScheduleBuilder。
例如: CronScheduleBuilder .cronSchedule(new CronExpression( expression))//采用cronScheduleBuilder生產 cronTrigger定時器
SimpleScheduleBuilder .SimpleSchedule()//采用SimpleScheduleBuilder生產 SimpleTrigger定時器
問題解決:
bean.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd "> <!-- 調度器 --> <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false"> </bean> </beans>
Mytask,java
/* * @(#)MyTask.java V0.0.1 2015-1-28, 下午8:34:14 * */ package com.jpgk.system.timer.config; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import com.jpgk.mq.model.TimerConfig; public class MyTask implements Job { private String id; private String category; private String destination; private String uri; private String clientid; private String content; private String expression; private String method; private TimerConfig timerConfig;// 一組timerconfig數據對象 // 任務執行覆寫 public void execute(JobExecutionContext context) throws JobExecutionException { TimerConfig timerConfig = (TimerConfig) context.getJobDetail() .getJobDataMap().get("timerconfig"); // 屬性賦值 initPrivate(timerConfig); System.out.println(timerConfig.getContent()); } // 初始化私有屬性,這個方法的存在是為了解決 線程池在每次反射實例化MyTask的時候使用無參構造函數,但任務需要這些私有屬性作為任務執行的參數 public void initPrivate(TimerConfig timerconfig) { timerConfig = timerconfig; category = timerConfig.getCategory().toUpperCase();// 取出任務類型 uri = timerConfig.getUri();// 取出請求路徑 destination = timerConfig.getDestination();// 取出目的地 clientid = timerConfig.getClientid();// 客戶ID expression = timerConfig.getExpression();// 表達式 content = timerConfig.getContent();// 請求參數,例如 a=1&b=2 method = timerConfig.getMethod().toUpperCase(); } }
調度頁面:Testabc.java
/* * @(#)Testabc.java V0.0.1 2015-1-28, 下午8:06:03 * * Copyright 2015 www.ifood517.com. All rights reserved. * www.ifood517.com PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package com.jpgk.mq.temp; import static org.quartz.JobBuilder.newJob; import static org.quartz.TriggerBuilder.newTrigger; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.quartz.CronExpression; import org.quartz.CronScheduleBuilder; import org.quartz.JobDetail; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.impl.StdScheduler; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.jpgk.mq.model.TimerConfig; import com.jpgk.mq.service.TimerConfigService; import com.jpgk.system.timer.config.MyTask; public class Testabc { private static StdScheduler stdScheduler; public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( "classpath:applicationContext.xml"); // 實例化線程池 stdScheduler = (StdScheduler) context.getBean("schedulerFactoryBean"); // 取出數據庫配置信息 TimerConfigService timerConfigService = (TimerConfigService) context .getBean("timerstaskservice"); List<TimerConfig> configs = timerConfigService.selectAll(); for (int i = 0; i < configs.size(); i++) { // 進行任務的調度 /* * String category = configs.get(i).getCategory();// 取出任務類型 String * uri = configs.get(i).getUri();// 取出請求路徑 String destination = * configs.get(i).getDestination();// 取出目的地 String clientid = * configs.get(i).getClientid();// 客戶ID String expression = * configs.get(i).getExpression();// 表達式 String content = * configs.get(i).getContent();// 請求參數,例如 a=1&b=2 String method = * configs.get(i).getMethod().toUpperCase();//請求方式 */switch (configs.get(i).getCategory()) { case "HTTP": // 必須要請求路徑 if (configs.get(i).getUri() == "" || configs.get(i).getUri() == null) { try { throw (new Exception("請求地址不能為空")); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 必須要請頻率 if (configs.get(i).getExpression() == "" || configs.get(i).getExpression() == null) { try { throw (new Exception("執行頻率不能為空")); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (configs.get(i).getMethod().toUpperCase().equals("GET")) { // 重新構造uri configs.get(i).setUri( configs.get(i).getUri() + "?" + configs.get(i).getContent()); } else { // post須考慮其他方式 System.out.println("aaaa"); } // System.out.println("category->"+category+"uri->"+uri+"destination->"+destination+"clientid->"+clientid+"expression->"+expression+"content->"+content+"method->"+method); break; default: break; } // 構造任務 JobDetail jobDetail = initJobdetail(configs.get(i).getClientid(), configs.get(i).getClientid() + "Group", configs.get(i)); // 構造定時器 Trigger trigger = initTriger(2, configs.get(i).getClientid(), configs.get(i).getClientid() + "Group", configs.get(i) .getExpression()); // 注冊定時器和任務 try { stdScheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { e.printStackTrace(); } } } /** * 實例化一個Trigger,根據type返回simple/Cron Trigger */ private static Trigger initTriger(int type, String name, String Groupname, String expression) { try { if (type == 1) { // 1 simpleTrigger // Simp } else if (type == 2) { // conTrigger Trigger trigger = newTrigger() .withIdentity(name, Groupname) .startNow() .withSchedule( CronScheduleBuilder .cronSchedule(new CronExpression( expression))).build(); return trigger; } } catch (Exception e) { } return null; } /** * 實例化一個JobDetail */ private static JobDetail initJobdetail(String name, String Groupname, TimerConfig timerConfig) { JobDetail jobDetail = newJob(MyTask.class) .withIdentity(name, Groupname).build(); // 在每次添加任務的時候要添加額外的參數,這里我傳一個對象進行任務私有屬性的初始化 jobDetail.getJobDataMap().put("timerconfig", timerConfig); return jobDetail; } }
以上代碼涉及到數據庫的數據,忽略即可。看看如何實例化JobDetail和Trigger的。留意下怎么在JobDetail中傳遞TimerConfig參數的。
但是只能說以上代碼只是個解決思路,但是任務調度真正的知識不限于這么少。例如,在任務執行過程中強制終止,休眠,更改表達式等。
供參考:任務調度暫停等實現
來自:http://blog.csdn.net/ccyours/article/details/43273663