前言:對於Quartz(kwɔrts)以前在公司用過,比較尷尬的是真的只是用過,寫個控制器在任務系統裏配置一下cron表達式就完事 https://github.com/songwie/task。從那天起我就對Quartz失去了興趣,後來在使用SpringBoot的時候瞭解到Scheduled(Spring 3.1以後支持),就用Scheduled搭建了一個簡單的任務系統。當時我就在想怎麼弄個到點就能執行的任務,由於用Scheduled註解有很大的侷限性,查閱了好多文檔(我好後悔我當初沒有學好英語,形成如今一直很反感英文文檔,每次都是搜索中文博客(開源中國,推酷,簡書segmentfault,scdn,.....),若是我英語給力,技術也不會這麼差)仍是沒有發現比較好的解決方案,當時正好作衆籌票務APP,好比用戶下單以後30分鐘沒有支付須要將該訂單的庫存回收並改變訂單狀態爲失效。若是輪詢1秒一次的話,這樣會頻繁查詢訂單表,將全部失效時間小於當前時間的而且未支付的全部訂單設置爲失效,這樣即不能作到及時,量比較多的話還會頻繁鎖表,訂單表對於票務網站自己就很高頻的,不論是下訂單,支付過程的狀態變動,仍是查詢訂單狀態。我當時採用了很low的方式,就是查詢訂單的時候,若是失效時間小於或者等於當前時間就update該ID的狀態。對於用戶來講沒有什麼變化,若是10條訂單中只有一個就只會更新一個。問題來了,若是該用戶沒有查詢訂單是否是狀態仍是未支付的狀態呢?因此我寫了一個1分鐘一次的輪詢來解決狀態問題。今天我不是來BB這種方案,其實Quartz除了CronTrigger還有SimpleTrigger。java
(百度百科)Quartz是一個徹底由java編寫的開源做業調度框架,是OpenSymphony開源組織在Job scheduling領域又一個開源項目,它能夠與J2EE與J2SE應用程序相結合也能夠單獨使用。Quartz能夠用來建立簡單或爲運行十個,百個,甚至是好幾萬個Jobs這樣複雜的程序。git
Scheduler – 與scheduler交互的主要API,這就是所謂的做業調度器github
Job – 你經過scheduler執行任務,你的任務類須要實現的接口;spring
JobDetail – 定義Job的實例;segmentfault
Trigger – 觸發Job的執行;併發
3.1 首先在主程序開啓對定時任務的支持框架
@EnableScheduling
3.2 編寫須要定時跑的代碼ide
@Scheduled(fixedRate=10000) public void test(){ System.out.println("程序跑來了"); }
經過@Scheduled註解 使用fixedRate時表示多少次執行一次,單位是毫秒網站
其實還有cron表達式屬性,具體設置能夠參考https://my.oschina.net/wangnian/blog/668209ui
我整合是設置時間到點執行,不是上面的cron表達式那種計劃時間或者循環執行。
4.1添加依賴
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.3</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency>
4.2 配置job交給spring管理
package com.yudianbank.task.config; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.scheduling.quartz.AdaptableJobFactory; import org.springframework.stereotype.Component; @Component public class JobFactory extends AdaptableJobFactory { @Autowired private AutowireCapableBeanFactory capableBeanFactory; @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { //調用父類的方法 Object jobInstance = super.createJobInstance(bundle); //進行注入 capableBeanFactory.autowireBean(jobInstance); return jobInstance; } }
主要目的就是解決job類 注入其餘service或者使用Spring組件
4.3配置javaconfig bean
@Configuration public class BeanConfig { @Autowired JobFactory jobFactory; /** * 註冊調度器 * * @return */ @Bean public SchedulerFactoryBean createSchedulerFactoryBean() { SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); schedulerFactoryBean.setJobFactory(jobFactory); return schedulerFactoryBean; } @Bean public JobDetailImpl createJobDetailImpl() { return new JobDetailImpl(); } }
4.4 任務方法
/** * 添加任務 * * @param job 任務類 * @param date 任務時間 * @param jobDetailName 任務消息名字 * @param triggerIdentity 觸發器的惟一名 * @param description 觸發器的描述 */ public synchronized void addJob(Job job, Date date, String jobDetailName, String triggerIdentity, String description, String url, String bodyParameter) { //job類的參數 JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("url", url); jobDataMap.put("bodyParameter", bodyParameter); jobDataMap.put("jobDetailName", jobDetailName); //這是job類的任務 jobDetail.setName(jobDetailName); jobDetail.setJobClass(job.getClass()); jobDetail.setJobDataMap(jobDataMap); //做業觸發器 Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerIdentity).withDescription(description) // .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(3))//循環間隔多久 .startAt(date)//執行時間 .build(); //做業調度器 try { Scheduler scheduler = schedulerFactoryBean.getScheduler(); //Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); } catch (SchedulerException e) { logger.error("任務調度器異常:", e);
4.5 編寫任務類(這裏就能夠注入service)
@Service public class JobTask implements Job { static final Logger logger = LoggerFactory.getLogger(JobTask.class); @Autowired ExecuteTaskService executeTaskService; @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { JobDataMap jobDataMap = jobExecutionContext.getMergedJobDataMap(); String url = jobDataMap.get("url").toString(); String bodyParameter = jobDataMap.get("bodyParameter").toString(); String jobDetailName = jobDataMap.get("jobDetailName").toString(); try { executeTaskService.execute(url, bodyParameter, jobDetailName); } catch (Exception ex) { } }
關於job的狀態數據(即JobDataMap)和併發性,還有一些地方須要注意。在job類上能夠加入一些註解,這些註解會影響job的狀態和併發性。
@DisallowConcurrentExecution:將該註解加到job類上,告訴Quartz不要併發地執行同一個job定義(這裏指特定的job類)的多個實例。請注意這裏的用詞。拿前一小節的例子來講,若是「SalesReportJob」類上有該註解,則同一時刻僅容許執行一個「SalesReportForJoe」實例,但能夠併發地執行「SalesReportForMike」類的一個實例。因此該限制是針對JobDetail的,而不是job類的。可是咱們認爲(在設計Quartz的時候)應該將該註解放在job類上,由於job類的改變常常會致使其行爲發生變化。
@PersistJobDataAfterExecution:將該註解加在job類上,告訴Quartz在成功執行了job類的execute方法後(沒有發生任何異常),更新JobDetail中JobDataMap的數據,使得該job(即JobDetail)在下一次執行的時候,JobDataMap中是更新後的數據,而不是更新前的舊數據。和 @DisallowConcurrentExecution註解同樣,儘管註解是加在job類上的,但其限制做用是針對job實例的,而不是job類的。由job類來承載註解,是由於job類的內容常常會影響其行爲狀態(好比,job類的execute方法須要顯式地「理解」其」狀態「)。
若是你使用了@PersistJobDataAfterExecution註解,咱們強烈建議你同時使用@DisallowConcurrentExecution註解,由於當同一個job(JobDetail)的兩個實例被併發執行時,因爲競爭,JobDataMap中存儲的數據極可能是不肯定的。
調用4.4的任務方法便可
博客地址:https://my.oschina.net/wangnian