Quartz使用監聽器插入定時任務執行日誌

来源:https://www.cnblogs.com/ndchao/archive/2022/12/04/16950093.html
-Advertisement-
Play Games

本文基於 newbeemall 項目升級Spring Boot3.0踩坑總結而來,附帶更新說明: Spring-Boot-3.0-發佈說明 Spring-Boot-3.0.0-M5-發佈說明 一. 編譯報錯,import javax.servlet.*; 不存在 這個報錯主要是Spring Boot ...


Quartz使用監聽器插入定時任務執行日誌

使用springboot,將監聽器交給spring容器管理,並像其中註入日誌服務類,環境準備工作實現任務調度需要導入兩個quartz的maven依賴

<dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.2</version>
 </dependency>
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
            <version>2.7.3</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-api</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
  </dependency>       
        

創建一個監聽器類,實現JobListener介面。


import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.JobListener;
import org.quartz.TriggerKey;
import org.springframework.stereotype.Component;

import java.util.Date;

@Slf4j
@Component
public class QuartzJobListener implements JobListener {

	private final QuartzRunningLogService logService;

	private static ThreadLocal<QuartzRunningLog> logThreadLocal = ThreadLocal.withInitial(QuartzRunningLog::new);

	public QuartzJobListener(QuartzRunningLogService logService) {
		this.logService = logService;
	}

	@Override
	public String getName() {
		return "jobLogListener";
	}
	/**
	*
	*Scheduler在JobDetail將要被執行時調用這個方法
	**/
	@Override
	public void jobToBeExecuted(JobExecutionContext context) {
		QuartzRunningLog quartzRunningLog = QuartzRunningLog
				.builder()
				.startTime(new Date())
				.build();
		logThreadLocal.set(quartzRunningLog);
	}
	/**
	*
	*Scheduler在JobDetail即將被執行,但又被TriggerListerner否決時會調用該方法
	**/
	@Override
	public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {

	}
	/**
	*
	*Scheduler在JobDetail被執行之後調用這個方法
	**/
	@Override
	public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
		JobKey jobKey = context.getJobDetail().getKey();
		TriggerKey triggerKey = context.getTrigger().getKey();
		Date fireTime = context.getFireTime();
		Class jobClass = context.getJobDetail().getJobClass();
		JobDataMap dataMap = context.getMergedJobDataMap();
		String taskName = (String) dataMap.get(CommonConst.TASK_NAME);
		String cronExpression = (String) dataMap.get(CommonConst.CRON_EXPRESSION);
		String jobName = (String) dataMap.get(CommonConst.JOB_NAME);
		log.info("JobClass:{},Job:{},Trigger:{},FireTime:{}", jobClass, jobKey, triggerKey, DateUtil.formatDateTime(fireTime));
		if (null != jobException) {
			//保存錯誤記錄
			QuartzRunningLog runningLog = logThreadLocal.get();
			runningLog.setJobName(jobName);
			runningLog.setCronExpression(cronExpression);
			runningLog.setEndTime(new Date());
			runningLog.setStatus("FAIL");
			runningLog.setTaskId(Long.valueOf(jobKey.getName()));
			runningLog.setTaskName(taskName);
			runningLog.setFailReason(jobException.getMessage());
			logService.save(runningLog);
			logThreadLocal.remove();
			return;
		}
		//保存執行記錄
		QuartzRunningLog runningLog = logThreadLocal.get();
		runningLog.setJobName(jobName);
		runningLog.setCronExpression(cronExpression);
		runningLog.setEndTime(new Date());
		runningLog.setStatus("SUCESS");
		runningLog.setTaskId(Long.valueOf(jobKey.getName()));
		runningLog.setTaskName(taskName);
		logService.save(runningLog);
		logThreadLocal.remove();
	}
}

quartzconfig配置類



import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

@Configuration
public class QuartzConfig {

	@Autowired
	private ApplicationContext applicationContext;

	/**
	 * Create the job factory bean
	 *
	 * @return Job factory bean
	 */
	@Bean
	public JobFactory jobFactory() {
		ApplicationContextHolder jobFactory = new ApplicationContextHolder();
		jobFactory.setApplicationContext(applicationContext);
		return jobFactory;
	}

	/**
	 * Create the Scheduler Factory bean
	 *
	 * @return scheduler factory object
	 */
	@Bean
	public SchedulerFactoryBean schedulerFactory() {
		SchedulerFactoryBean factory = new SchedulerFactoryBean();
		factory.setAutoStartup(true);
		factory.setSchedulerName("Scheduler");
		factory.setOverwriteExistingJobs(true);
		factory.setJobFactory(jobFactory());
		return factory;
	}

	/**
	 * Create the Scheduler bean
	 *
	 * @param logService
	 * @return Scheduler
	 * @throws SchedulerException
	 */
	@Bean
	public Scheduler scheduler(@Autowired QuartzRunningLogService logService) throws SchedulerException {
        //在這裡註入日誌服務類且激活監聽器,如果直接在監聽器類裡面使用@Autowired會出現註入為null
		schedulerFactory().getScheduler().getListenerManager().addJobListener(new QuartzJobListener(logService));
		return schedulerFactory().getScheduler();
	}

}

容器工具類


import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContextHolder extends SpringBeanJobFactory
		implements ApplicationContextAware {
	private static ApplicationContext context;
	private transient AutowireCapableBeanFactory beanFactory;

	@Override
	public void setApplicationContext(final ApplicationContext context) {
		beanFactory = context.getAutowireCapableBeanFactory();
		ApplicationContextHolder.context = context;
	}

	@Override
	protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
		final Object job = super.createJobInstance(bundle);
		beanFactory.autowireBean(job);
		return job;
	}

	public static ApplicationContext getContext() {
		return context;
	}
}


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 如果你接手了別人的代碼工程,卻發現對方使用的 python 版本或者依賴庫都和你的環境不相容時,怎麼辦?打算卸掉自己原來的那一套環境再重來嗎?真麻煩! ...
  • 該筆記整理至尚矽谷周陽老師的SpringCloud課程SpringCloud Alibaba篇 SpringCloud Alibaba入門簡介 Spring Cloud Netflix 項目進入維護模式,Spring Cloud Netflix 將不再開發新的組件。Spring Cloud 版本迭代 ...
  • ###Http Http (超文本輸出協議) 是一種分散式、協作式和超媒體信息系統的應用層協議,它通常運行在TCP之上,網際網路應用最廣泛的便是Http協議,所有www都遵循這個標準。主要用於Web 瀏覽器與 Web 伺服器之間的通信而設計的,但也可以用於其他目的,是一個基於 TCP/IP 通信協議來 ...
  • 隨著跨境獨立站的流行,中英雙語的公司官網越來越受到重視。 此項目是基於開源CMS開發出的中英文雙語外貿企業網站內容管理系統,命名HanCMS HanCMS 漢CMS中英雙語多語種外貿網站系統,是一個輕量級的網站系統,訪問速度極快,使用簡單。程式代碼簡潔嚴謹,完全免費開源。可用於建設各種類型的中英文網 ...
  • 原文: JDK中內嵌JS引擎介紹及使用 - Stars-One的雜貨小窩 最近研究閱讀這個APP,其主要功能就是通過一個個書源,從而實現移動端閱讀的體驗 比如說某些線上小說閱讀網站,會加上相應的廣告,從而影響用戶閱讀體驗,於是閱讀這個APP就是做了類似凈化閱讀體驗 但是小說閱讀網站千千萬萬,如果去適 ...
  • 概述 ZYNQ分為PS和PL兩部分,PS端即ARM,PL即FPGA。在使用ZYNQ的時候不免需要PS和PL端進行通信。大多是情況下PS作為主端,PL作為從端,通過AXI匯流排實現PS-PL端的通信。本文主要介紹PL(即FPGA)如何配置的。 Block Design創建 1.點擊Create Bloc ...
  • 動態代理和責任鏈設計模式適用範圍廣,在Spring和MyBatis有著重要的應用,比如SpringAOP、Mybatis的插件技術,想要搞懂當中的技術原理必須掌握上面兩個設計模式。 代理模式可以理解為您要操作一個對象,但是要經過這個對象的“代理”對象去操作。就好似你在一家軟體公司做開發,客戶發現程式 ...
  • 說明: 1. 本文基於Spring-Framework 5.1.x版本講解 2. 建議讀者對Mybatis有基本的使用經驗 概述 這一篇我們講講org.springframework.beans.factory.FactoryBean介面,這個介面功能非常強大,可以集成不同的中間件或組件到Sprin ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...