一、前言
一般项目时Quartz作为日常业务的定时调度,系统时间符合corn表达式后,系统自动执行对应的自定义job。某些业务场景下,在未到达指定时间时,需要该job立即执行。
二、定时任务一般执行
一般所有业务类定时任务都会extends 公共定时任务执行方法BaseTimeJob 。BaseTimeJob extend QuartzJobBean,重写executeInternal方法,实现层增加相关日志信息的打印,如任务开始、结束、返回处理等。
public abstract class BaseZjJob extends QuartzJobBean {
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
**//日志打印省略
JobDetail jobDetail = jobExecutionContext.getJobDetail();
String jobTask = jobDetail.getKey().getName();
**//异常捕获省略
log.info("Job " + jobTask + " start");
ResultEnum resultEnum = this.taskPerform(jobExecutionContext);
**//返回处理省略
log.info("Job " + jobTask + " end");
}
}
protected abstract ResultEnum taskPerform(JobExecutionContext var1) throws Exception;
}
一般业务类定时任务JobA ,则会Override如上taskPerform方法
public class JobA extends BaseTimeJob {
@Override
protected ResultEnum taskPerform(JobExecutionContext jobExecutionContext) throws Exception {
//1.读取参数
JobDetail jobDetail = jobExecutionContext.getJobDetail();
String fileName= jobDetail.getJobDataMap().getString("fileName");
**//业务实现逻辑
return ResultEnum.SUCCEED;
}
}
三、定时任务立即执行
定时任务立即执行要求不影响其日常的常规触点执行,所以一般采用暴露给前端的接口完成。
public void executeJob(String jobName, String group, Map<String, Object> param) throws Exception {
JobKey jobKey = JobKey.jobKey(jobName, group);
JobDataMap jobDataMap = new JobDataMap(param);
this.scheduler.triggerJob(jobKey, jobDataMap);//定时任务创建的sheduler
}
四、问题暴露与解决
如上代码在实际测试中发现,立即执行调用时的参数无法在基本jobA中获取到,其仍然获取的是初始化加载的参数MAP。
1)查看scheduler.triggerJob方法,其表述如下,即其传入的参数Map会置于job(也是个大Map)的第一层,key为jobDataMap。而我们一般初始化的默认参数是放在job下面的JobDetail中,key为jobDataMap,等于是第二层。
/**
* Trigger the identified <code>{@link org.quartz.JobDetail}</code>
* (execute it now).
*
* @param data the (possibly <code>null</code>) JobDataMap to be
* associated with the trigger that fires the job immediately.
*/
void triggerJob(JobKey jobKey, JobDataMap data)
throws SchedulerException;
2)解决方式,jobA任务执行是获取jobDataMao的方法更换为:getMergedJobDataMap();
/**
* <p>
* Get the convenience <code>JobDataMap</code> of this execution context.
* </p>
*
* <p>
* The <code>JobDataMap</code> found on this object serves as a convenience -
* it is a merge of the <code>JobDataMap</code> found on the
* <code>JobDetail</code> and the one found on the <code>Trigger</code>, with
* the value in the latter overriding any same-named values in the former.
* <i>It is thus considered a 'best practice' that the execute code of a Job
* retrieve data from the JobDataMap found on this object.</i>
* </p>
*
* <p>NOTE: Do not expect value 'set' into this JobDataMap to somehow be set
* or persisted back onto a job's own JobDataMap - even if it has the
* <code>@PersistJobDataAfterExecution</code> annotation.
* </p>
*
* <p>
* Attempts to change the contents of this map typically result in an
* <code>IllegalStateException</code>.
* </p>
*
*/
public JobDataMap getMergedJobDataMap();
3)重试,问题解决!