一、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
org.quartz-scheduler
quartz
2.2.1
org.quartz-scheduler
quartz-jobs
2.2.1
1.spring配置(其实只要这一行足矣,去掉了原先"taskJob"、"myTrigger"等配置):
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, 禁止并发执行。
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
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
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中配置改为如下:
<
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类。