SpringBoot集成quartz的教程很多,這里不在贅述了。
自定義監(jiān)聽器
以下是自定義的監(jiān)聽器,繼承了JobListenerSupport,主要就是做任務(wù)執(zhí)行的日志記錄。
package com.zdww.szzf.investment.qrtz;
import cn.hutool.extra.spring.SpringUtil;
import com.zdww.szzf.investment.entity.SysJobsLog;
import com.zdww.szzf.investment.service.ISysJobsLogService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.listeners.JobListenerSupport;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 任務(wù)監(jiān)聽
*
* @version 1.0
* @date 2024/1/8 09:39
**/
@Slf4j
public class ScheduleListener extends JobListenerSupport {
private static final ThreadLocal<LocalDateTime> threadLocal = new ThreadLocal<>();
/**
* 任務(wù)執(zhí)行成功
*/
private static final String STATUS_SUCCESS = "1";
/**
* 任務(wù)執(zhí)行失敗
*/
private static final String STATUS_FAIL = "0";
@Override
public String getName() {
return "ScheduleListener";
}
@Override
public void jobToBeExecuted(JobExecutionContext context) {
LocalDateTime now = LocalDateTime.now();
threadLocal.set(now);
log.warn("任務(wù)執(zhí)行開始,開始時(shí)間: {}", now);
}
@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
LocalDateTime end = LocalDateTime.now();
LocalDateTime start = threadLocal.get();
long millis = Duration.between(start, end).toMillis();
threadLocal.remove();
SysJobsLog jobsLog = new SysJobsLog();
jobsLog.setJobId(Long.valueOf(context.getJobDetail().getKey().getName()));
jobsLog.setJobGroup(context.getJobDetail().getKey().getGroup());
jobsLog.setJobName(context.getJobDetail().getDescription());
jobsLog.setStartTime(start);
jobsLog.setEndTime(end);
if (Objects.nonNull(jobException)) {
jobsLog.setStatus(STATUS_FAIL);
jobsLog.setExceptionInfo(jobException.getMessage());
log.warn("任務(wù)執(zhí)行結(jié)束, 執(zhí)行失敗, 結(jié)束時(shí)間: {}", end);
} else {
jobsLog.setStatus(STATUS_SUCCESS);
jobsLog.setJobMessage("執(zhí)行成功, 耗時(shí)" + millis + "毫秒");
log.warn("任務(wù)執(zhí)行結(jié)束, 執(zhí)行成功, 結(jié)束時(shí)間: {}", end);
}
SpringUtil.getBean(ISysJobsLogService.class).save(jobsLog);
}
}
創(chuàng)建任務(wù)
創(chuàng)建任務(wù)的時(shí)候注冊(cè)一個(gè)全局的監(jiān)聽器
public void addJob(SysJobs job) {
try {
Class<? extends Job> aClass = (Class<? extends Job>) Class.forName(JOB_PACKAGE + job.getInvokeTarget());
JobDetail jobDetail = JobBuilder.newJob(aClass)
.withIdentity(String.valueOf(job.getId()), job.getJobGroup())
// 此處是把任務(wù)名稱存放在Description,方便記錄日志獲取
.withDescription(job.getJobName())
.build();
// 添加參數(shù)
String extend = job.getExtend();
if (StringUtils.isNotBlank(extend)) {
jobDetail.getJobDataMap().putAll(JSON.parseObject(extend));
}
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression())
// 過期任務(wù)丟棄
.withMisfireHandlingInstructionDoNothing();
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
.withIdentity(String.valueOf(job.getId()), job.getJobGroup())
.withDescription(job.getJobName())
.withSchedule(cronScheduleBuilder)
.build();
JobKey jobKey = JobKey.jobKey(String.valueOf(job.getId()), job.getJobGroup());
scheduler.getListenerManager().addJobListener(new ScheduleListener());
// 如果已經(jīng)存在則刪除
if (scheduler.checkExists(jobKey)) {
scheduler.deleteJob(jobKey);
}
scheduler.scheduleJob(jobDetail, cronTrigger);
// 狀態(tài)是禁用的時(shí)候任務(wù)暫停
if (StringUtils.equals(job.getStatus(), StatusEnum.DISABLED.getCode())) {
scheduler.pauseJob(jobKey);
}
} catch (Exception e) {
log.error("任務(wù)添加異常", e);
throw new BusinessException("任務(wù)添加異常");
}
}
問題:任務(wù)在執(zhí)行的時(shí)候監(jiān)聽器正常執(zhí)行,但是在項(xiàng)目重啟后監(jiān)聽器不執(zhí)行了...
解決方案
https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/tutorial-lesson-07.html
在tutorial中看到了這一段描述恍然大悟
Listeners are registered with the scheduler during run time, and are NOT stored in the JobStore along with the jobs and triggers. This is because listeners are typically an integration point with your application. Hence, each time your application runs, the listeners need to be re-registered with the scheduler.
package com.zdww.szzf.investment.listener;
import com.zdww.szzf.investment.qrtz.ScheduleListener;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobListener;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* quartz 監(jiān)聽器注冊(cè)
* Listeners are registered with the scheduler during run time,
* and are NOT stored in the JobStore along with the jobs and triggers.
* This is because listeners are typically an integration point with your application.
* Hence, each time your application runs, the listeners need to be re-registered with the scheduler.
*
* @version 1.0
* @date 2024/1/11 14:33
**/
@Slf4j
@Component
public class JobListenerRegister implements ApplicationListener<ApplicationStartedEvent> {
@Autowired
private Scheduler scheduler;
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
try {
ScheduleListener scheduleListener = new ScheduleListener();
scheduler.getListenerManager().addJobListener(scheduleListener);
log.info("Scheduler 全局監(jiān)聽器注冊(cè)成功");
} catch (SchedulerException e) {
log.error("Scheduler 全局監(jiān)聽器注冊(cè)失敗", e);
throw new RuntimeException(e);
}
}
}
這樣項(xiàng)目重新啟動(dòng)后監(jiān)聽器可以正常工作了。