擴展:spring3整合quartz2,實現動態添加、刪除定時任務

一、Quartz簡介 
Quartz大致可分爲三個主要的核心:
    1、調度器Scheduler:是一個計劃調度器容器,容器裏面可以盛放衆多的JobDetail和Trigger,當容器啓動後,裏面的每個JobDetail都會根據Trigger按部就班自動去執行.
    2、任務Job:要執行的具體內容。JobDetail:具體的可執行的調度程序,包含了這個任務調度的方案和策略。
    3、觸發器Trigger:調度參數的配置,什麼時候去執行調度。
原理:
可以這麼理解它的原理:調度器就相當於一個容器,裝載着任務和觸發器。任務和觸發器又是綁定在一起的,然而一個任務可以對應多個觸發器,但一個觸發器卻只能對應一個任務。當JobDetail和Trigger在scheduler容器上註冊後,形成了裝配好的任務作業(JobDetail和Trigger所組成的一對兒),就可以伴隨容器啓動而調度執行了。
二、與spring的整合
    本文的用的是quartz-2.2.1與spring-3.2.2。之所以在這裏特別對版本作一下說明,是因爲spring和quartz的整合對版本是有要求的。spring3.1以下的版本必須使用quartz1.x系列,3.1以上的版本才支持quartz 2.x,不然會出錯。原因主要是:spring對於quartz的支持實現,org.springframework.scheduling.quartz.CronTriggerBean繼承了 org.quartz.CronTrigger,在quartz1.x系列中org.quartz.CronTrigger是個類,而在 quartz2.x系列中org.quartz.CronTrigger變成了接口,從而造成無法用spring的方式配置quartz的觸發器 (trigger)。
spring中使用quartz有兩種方式,具體請看http://blog.csdn.net/liuxiao723846/article/details/46879077
三、動態整合
   上面的整合只能應付簡單的需求,但很多時候我們遇到的是需要動態的添加、暫停、修改任務。而spring中所提供的定時任務組件卻只能夠通過修改xml中trigger的配置才能控制定時任務的時間以及任務的啓用或停止,這在帶給我們方便的同時也失去了動態配置任務的靈活性。
   所以我們就得換種方式來解決。把任務與cronExpression存放在數據庫中,最大化減少xml配置,創建一個工廠類,在實際調用時把任務的相關信息通過參數方式傳入,由該工廠類根據任務信息來具體執行需要的操作,從而方便我們的動態修改。

Maven

	<!-- quartz -->
	<dependency>
        	<groupId>org.quartz-scheduler</groupId>
        	<artifactId>quartz</artifactId>
        	<version>2.2.1</version>
    	</dependency>
		<dependency>
        	<groupId>org.quartz-scheduler</groupId>
        	<artifactId>quartz-jobs</artifactId>
        	<version>2.2.1</version>
    	</dependency>

1.spring配置(其實只要這一行足矣,去掉了原先"taskJob"、"myTrigger"等配置):
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" />
2.任務執行入口(就是在這裏執行quartz容器中的任務的),實現Job接口,類似工廠類:
package com.es.quartz;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import com.es.entry.ExportTask;
import com.es.service.TaskExportService;
import com.es.utils.exception.BusinessException;
/**
 * 定時任務運行工廠類
 * @author Administrator
 *Quartz定時任務默認都是併發執行的,不會等待上一次任務執行完畢,只要間隔時間到就會執行, 如果定時任執行太長,會長時間佔用資源,導致其它任務堵塞。
     1.在Spring中這時需要設置concurrent的值爲false, 禁止併發執行。
     
      <property name="concurrent" value="true" />
     2.當不使用spring的時候就需要在Job的實現類上加@DisallowConcurrentExecution的註釋
     
     設置@DisallowConcurrentExecution以後程序會等任務執行完畢以後再去執行,否則會在3秒時再啓用新的線程執行
 */
@DisallowConcurrentExecution
public class QuartzJob implements Job {
     private Logger myLogger = LogManager.getLogger("QuartzJobFactory");
     @Autowired
     private TaskExportService taskExportService;
     @Override
     public void execute(JobExecutionContext context) throws JobExecutionException {
           try {
              ExportTask task =   (ExportTask) context.getJobDetail().getJobDataMap().get("task");
//            context.getJobDetail().getJobDataMap().v
              myLogger.info(task.getName()+":任務成功運行");
              if("exact".equals(task.getSearchType())){
                   taskExportService.runTaskAutomaticForExact(task);
              }
           } catch (BusinessException e) {
               e.printStackTrace();
           }
        myLogger.info("任務成功運行完成"); 
     }
}

3、創建任務類。既然要動態修改任務,那任務就得保存在某個地方。一個po類
package com.es.entry;
public class ExportTask {
     private Integer id;
     private String name;//任務名
     private String export_cron;//cron表達式
     private String start_time;//開始時間
     private String run_time;//運行時長
     private Integer status;//'狀態(0:未運行,1:運行中,2:運行完成)
     private String file_url;//文件地址
     private String params;//查詢參數
     private Integer type;//調用類型(0:手動,1:週期)
     private String searchType;//檢索類型
     private Integer createUserId;//創建人id
     public Integer getId() {
          return id;
     }
     public void setId(Integer id) {
          this.id = id;
     }
     public String getName() {
          return name;
     }
     public void setName(String name) {
          this.name = name;
     }
     public String getExport_cron() {
          return export_cron;
     }
     public void setExport_cron(String export_cron) {
          this.export_cron = export_cron;
     }
     public String getStart_time() {
          return start_time;
     }
     public void setStart_time(String start_time) {
          this.start_time = start_time;
     }
     public String getRun_time() {
          return run_time;
     }
     public void setRun_time(String run_time) {
          this.run_time = run_time;
     }
     public Integer getStatus() {
          return status;
     }
     public void setStatus(Integer status) {
          this.status = status;
     }
     public String getFile_url() {
          return file_url;
     }
     public void setFile_url(String file_url) {
          this.file_url = file_url;
     }
     public String getParams() {
          return params;
     }
     public void setParams(String params) {
          this.params = params;
     }
     public Integer getType() {
          return type;
     }
     public void setType(Integer type) {
          this.type = type;
     }
     public String getSearchType() {
          return searchType;
     }
     public void setSearchType(String searchType) {
          this.searchType = searchType;
     }
     public Integer getCreateUserId() {
          return createUserId;
     }
     public void setCreateUserId(Integer createUserId) {
          this.createUserId = createUserId;
     }
}
4.在這裏進行任務的增刪改查
接口:
package com.es.quartz;

import java.util.List;
import com.es.entry.ExportTask;
import com.es.utils.exception.BusinessException;
public interface QuartzManager {
     /**
      * 向容器中添加任務
      * @param task
      * @throws BusinessException
      */
     public void addJob(ExportTask task) throws BusinessException;
     /**
      * 從容器中刪除任務
      * @param jobnames
      * @throws BusinessException
      */
     public void removeJob(List<String> jobnames) throws BusinessException;
     
}
實現類:
package com.es.quartz.impl;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;
import com.es.entry.ExportTask;
import com.es.quartz.QuartzJob;
import com.es.quartz.QuartzManager;
import com.es.utils.exception.BusinessException;
@Component
public class QuartzManagerImpl implements QuartzManager {
     private Logger log = LogManager.getLogger("QuartzManagerImpl");
     @Autowired
     private SchedulerFactoryBean schedulerFactoryBean;
     @Override
     public void addJob(ExportTask task) throws BusinessException {
          log.info("addJob start:"+(task.getName()+"@"+task.getId()));
          Scheduler scheduler =schedulerFactoryBean.getScheduler();
          //任務構建withIdentity:相當於給任務起了個名字
          JobDetail jobDetail =JobBuilder.newJob(QuartzJob.class).withIdentity(new JobKey(task.getName()+"@"+task.getId())).build();
          
          jobDetail.getJobDataMap().put("task", task);
          //表達式調度構建器
          CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getExport_cron());
          //按新的cronExpression表達式構建一個新的trigger:withIdentity:相當於給trigger起了個名字
          CronTrigger trigger = TriggerBuilder.newTrigger().withSchedule(scheduleBuilder).withIdentity(new TriggerKey(task.getName()+"@"+task.getId())).build();
          try {
              scheduler.scheduleJob(jobDetail, trigger);
          } catch (SchedulerException e) {
              throw new BusinessException(e, (task.getName()+"@"+task.getId())+":quartz添加job異常");
          }
     }
     @Override
     public void removeJob(List<String> jobnames) throws BusinessException {
          log.info("removeJob start :"+jobnames.size());
          if(null==jobnames||jobnames.isEmpty()){
              throw new BusinessException(jobnames+":quartz刪除job 參數異常");
          }
          Scheduler scheduler =schedulerFactoryBean.getScheduler();
          try {
              for(String name:jobnames){
                   scheduler.pauseTrigger(new TriggerKey(name));// 停止觸發器
                   scheduler.unscheduleJob(new TriggerKey(name));//移除觸發器
                   scheduler.deleteJob(new JobKey(name));//刪除任務 
              }
          } catch (SchedulerException e) {
              throw new BusinessException(e, jobnames+":quartz刪除job異常");
          }
     }
}
5.quartz的Job中注入spring對象
一般情況下,quartz的job中使用autowired註解注入的對象爲空,這時候我們就要使用spring-quartz提供的AdaptableJobFactory類自定義一個類:
package com.es.quartz;
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;
/**
* 一般情況下,quartz的job中使用autowired註解注入的對象爲空,
* 這時候我們就要使用spring-quartz提供的AdaptableJobFactory類。
* @author Administrator
*
*/
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;  
     }
}

然後將spring中配置改爲如下:
<!-- quartz -->
     <bean id="jobFactory" class="com.es.quartz.JobFactory"></bean>
     <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >
          <property name="jobFactory" ref="jobFactory"></property>
     </bean>
這時候我們就可以在Job的實現類中使用autowired注入service對象了,如QuartzJob類。
發佈了32 篇原創文章 · 獲贊 16 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章