一、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中所提供的定時任務組件卻只能夠通過修改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類。