SpringBoot集成quartz注冊(cè)全局監(jiān)聽器,項(xiàng)目重啟后監(jiān)聽器不執(zhí)行

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)聽器可以正常工作了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容