日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > C# >内容正文

C#

c#quartz触发_SpringBoot集成Quartz实现定时任务

發(fā)布時間:2023/12/10 C# 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c#quartz触发_SpringBoot集成Quartz实现定时任务 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1 需求

在我的前后端分離的實驗室管理項目中,有一個功能是學(xué)生狀態(tài)統(tǒng)計。我的設(shè)計是按天統(tǒng)計每種狀態(tài)的比例。為了便于計算,在每天0點,系統(tǒng)需要將學(xué)生的狀態(tài)重置,并插入一條數(shù)據(jù)作為一天的開始狀態(tài)。另外,考慮到學(xué)生的請假需求,請假的申請往往是提前做好,等系統(tǒng)時間走到實際請假時間的時候,系統(tǒng)要將學(xué)生的狀態(tài)修改為請假。

顯然,這兩個子需求都可以通過定時任務(wù)實現(xiàn)。在網(wǎng)上略做搜索以后,我選擇了比較流行的定時任務(wù)框架Quartz。

2 Quartz

Quartz是一個定時任務(wù)框架,其他介紹網(wǎng)上也很詳盡。這里要介紹一下Quartz里的幾個非常核心的接口。

2.1 Scheduler接口

Scheduler翻譯成調(diào)度器,Quartz通過調(diào)度器來注冊、暫停、刪除Trigger和JobDetail。Scheduler還擁有一個SchedulerContext,顧名思義就是上下文,通過SchedulerContext我們可以獲取到觸發(fā)器和任務(wù)的一些信息。

2.2 Trigger接口

Trigger可以翻譯成觸發(fā)器,通過cron表達(dá)式或是SimpleScheduleBuilder等類,指定任務(wù)執(zhí)行的周期。系統(tǒng)時間走到觸發(fā)器指定的時間的時候,觸發(fā)器就會觸發(fā)任務(wù)的執(zhí)行。

2.3 JobDetail接口

Job接口是真正需要執(zhí)行的任務(wù)。JobDetail接口相當(dāng)于將Job接口包裝了一下,Trigger和Scheduler實際用到的都是JobDetail。

3 SpringBoot官方文檔解讀

SpringBoot官方寫了 spring-boot-starter-quartz 。使用過SpringBoot的同學(xué)都知道這是一個官方提供的啟動器,有了這個啟動器,集成的操作就會被大大簡化。

現(xiàn)在我們來看一看SpingBoot2.2.6官方文檔,其中第4.20小節(jié) Quartz Scheduler 就談到了Quartz,但很可惜一共只有兩頁不到的內(nèi)容,先來看看這么精華的文檔里能學(xué)到些什么。

Spring Boot offers several conveniences for working with the Quartz scheduler, including the spring-boot-starter-quartz “Starter”. If Quartz is available, a Scheduler is auto-configured (through the SchedulerFactoryBean abstraction). Beans of the following types are automatically picked up and associated with the Scheduler: ? JobDetail: defines a particular Job. JobDetail instances can be built with the JobBuilder API. ? Calendar. ? Trigger: defines when a particular job is triggered.

翻譯一下:

SpringBoot提供了一些便捷的方法來和Quartz協(xié)同工作,這些方法里面包括`spring-boot-starter-quartz`這個啟動器。如果Quartz可用,Scheduler會通過SchedulerFactoryBean這個工廠bean自動配置到SpringBoot里。 JobDetail、Calendar、Trigger這些類型的bean會被自動采集并關(guān)聯(lián)到Scheduler上。 Jobs can define setters to inject data map properties. Regular beans can also be injected in a similar manner.

翻譯一下:

Job可以定義setter(也就是set方法)來注入配置信息。也可以用同樣的方法注入普通的bean。

下面是文檔里給的示例代碼,我直接完全照著寫,拿到的卻是null。不知道是不是我的使用方式有誤。后來仔細(xì)一想,文檔的意思應(yīng)該是在創(chuàng)建Job對象之后,調(diào)用set方法將依賴注入進(jìn)去。但后面我們是通過框架反射生成的Job對象,這樣做反而會搞得更加復(fù)雜。最后還是決定采用給Job類加@Component注解的方法。

文檔的其他篇幅就介紹了一些配置,但是介紹得也不全面,看了幫助也并不是很大。詳細(xì)的配置可以參考w3school的 Quartz配置 。

4 SpringBoot集成Quartz

4.1 建表

我選擇將定時任務(wù)的信息保存在數(shù)據(jù)庫中,優(yōu)點是顯而易見的,定時任務(wù)不會因為系統(tǒng)的崩潰而丟失。

建表的sql語句在Quartz的github中可以找到,里面有針對每一種常用數(shù)據(jù)庫的sql語句,具體地址是: Quartz數(shù)據(jù)庫建表sql 。

建表以后,可以看到數(shù)據(jù)庫里多了11張表。我們完全不需要關(guān)心每張表的具體作用,在添加刪除任務(wù)、觸發(fā)器等的時候,Quartz框架會操作這些表。

4.2 引入依賴

在 pom.xml 里添加依賴。

<!-- quartz 定時任務(wù) --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId><version>2.2.6.RELEASE</version> </dependency>

4.3 配置quartz

在 application.yml 中配置quartz。相關(guān)配置的作用已經(jīng)寫在注解上。

# spring的datasource等配置未貼出 spring:quartz:# 將任務(wù)等保存化到數(shù)據(jù)庫job-store-type: jdbc# 程序結(jié)束時會等待quartz相關(guān)的內(nèi)容結(jié)束wait-for-jobs-to-complete-on-shutdown: true# QuartzScheduler啟動時更新己存在的Job,這樣就不用每次修改targetObject后刪除qrtz_job_details表對應(yīng)記錄overwrite-existing-jobs: true# 這里居然是個map,搞得智能提示都沒有,佛了properties:org:quartz:# scheduler相關(guān)scheduler:# scheduler的實例名instanceName: schedulerinstanceId: AUTO# 持久化相關(guān)jobStore:class: org.quartz.impl.jdbcjobstore.JobStoreTXdriverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate# 表示數(shù)據(jù)庫中相關(guān)表是QRTZ_開頭的tablePrefix: QRTZ_useProperties: false# 線程池相關(guān)threadPool:class: org.quartz.simpl.SimpleThreadPool# 線程數(shù)threadCount: 10# 線程優(yōu)先級threadPriority: 5threadsInheritContextClassLoaderOfInitializingThread: true

4.4 注冊周期性的定時任務(wù)

第1節(jié)中提到的第一個子需求是在每天0點執(zhí)行的,是一個周期性的任務(wù),任務(wù)內(nèi)容也是確定的,所以直接在代碼里注冊JobDetail和Trigger的bean就可以了。當(dāng)然,這些JobDetail和Trigger也是會被持久化到數(shù)據(jù)庫里。

/*** Quartz的相關(guān)配置,注冊JobDetail和Trigger* 注意JobDetail和Trigger是org.quartz包下的,不是spring包下的,不要導(dǎo)入錯誤*/ @Configuration public class QuartzConfig {@Beanpublic JobDetail jobDetail() {JobDetail jobDetail = JobBuilder.newJob(StartOfDayJob.class).withIdentity("start_of_day", "start_of_day").storeDurably().build();return jobDetail;}@Beanpublic Trigger trigger() {Trigger trigger = TriggerBuilder.newTrigger().forJob(jobDetail()).withIdentity("start_of_day", "start_of_day").startNow()// 每天0點執(zhí)行.withSchedule(CronScheduleBuilder.cronSchedule("0 0 0 * * ?")).build();return trigger;} }

builder類創(chuàng)建了一個JobDetail和一個Trigger并注冊成為Spring bean。從第3節(jié)中摘錄的官方文檔中,我們已經(jīng)知道這些bean會自動關(guān)聯(lián)到調(diào)度器上。需要注意的是JobDetail和Trigger需要設(shè)置組名和自己的名字,用來作為唯一標(biāo)識。當(dāng)然,JobDetail和Trigger的唯一標(biāo)識可以相同,因為他們是不同的類。

Trigger通過cron表達(dá)式指定了任務(wù)執(zhí)行的周期。對cron表達(dá)式不熟悉的同學(xué)可以百度學(xué)習(xí)一下。

JobDetail里有一個StartOfDayJob類,這個類就是Job接口的一個實現(xiàn)類,里面定義了任務(wù)的具體內(nèi)容,看一下代碼:

@Component public class StartOfDayJob extends QuartzJobBean {private StudentService studentService;@Autowiredpublic StartOfDayJob(StudentService studentService) {this.studentService = studentService;}@Overrideprotected void executeInternal(JobExecutionContext jobExecutionContext)throws JobExecutionException {// 任務(wù)的具體邏輯} }

網(wǎng)上很多博客也是這么介紹的。但是根據(jù)我的實際測試,這樣寫可以完成依賴注入,但我還不知道它的實現(xiàn)原理。

4.5 注冊無周期性的定時任務(wù)

第1節(jié)中提到的第二個子需求是學(xué)生請假,顯然請假是不定時的,一次性的,而且不具有周期性。

4.5節(jié)與4.4節(jié)大體相同,但是有兩點區(qū)別:

  • Job類需要獲取到一些數(shù)據(jù)用于任務(wù)的執(zhí)行;
  • 任務(wù)執(zhí)行完成后刪除Job和Trigger。

業(yè)務(wù)邏輯是在老師批準(zhǔn)學(xué)生的請假申請時,向調(diào)度器添加Trigger和JobDetail。

實體類:

public class LeaveApplication {@TableId(type = IdType.AUTO)private Integer id;private Long proposerUsername;@JsonFormat( pattern = "yyyy-MM-dd HH:mm",timezone="GMT+8")private LocalDateTime startTime;@JsonFormat( pattern = "yyyy-MM-dd HH:mm",timezone="GMT+8")private LocalDateTime endTime;private String reason;private String state;private String disapprovedReason;private Long checkerUsername;private LocalDateTime checkTime;// 省略getter、setter }

Service層邏輯,重要的地方已在注釋中說明。

@Service public class LeaveApplicationServiceImpl implements LeaveApplicationService {@Autowiredprivate Scheduler scheduler;// 省略其他方法與其他依賴/*** 添加job和trigger到scheduler*/private void addJobAndTrigger(LeaveApplication leaveApplication) {Long proposerUsername = leaveApplication.getProposerUsername();// 創(chuàng)建請假開始JobLocalDateTime startTime = leaveApplication.getStartTime();JobDetail startJobDetail = JobBuilder.newJob(LeaveStartJob.class)// 指定任務(wù)組名和任務(wù)名.withIdentity(leaveApplication.getStartTime().toString(),proposerUsername + "_start")// 添加一些參數(shù),執(zhí)行的時候用.usingJobData("username", proposerUsername).usingJobData("time", startTime.toString()).build();// 創(chuàng)建請假開始任務(wù)的觸發(fā)器// 創(chuàng)建cron表達(dá)式指定任務(wù)執(zhí)行的時間,由于請假時間是確定的,所以年月日時分秒都是確定的,這也符合任務(wù)只執(zhí)行一次的要求。String startCron = String.format("%d %d %d %d %d ? %d",startTime.getSecond(),startTime.getMinute(),startTime.getHour(),startTime.getDayOfMonth(),startTime.getMonth().getValue(),startTime.getYear());CronTrigger startCronTrigger = TriggerBuilder.newTrigger()// 指定觸發(fā)器組名和觸發(fā)器名.withIdentity(leaveApplication.getStartTime().toString(),proposerUsername + "_start").withSchedule(CronScheduleBuilder.cronSchedule(startCron)).build();// 將job和trigger添加到scheduler里try {scheduler.scheduleJob(startJobDetail, startCronTrigger);} catch (SchedulerException e) {e.printStackTrace();throw new CustomizedException("添加請假任務(wù)失敗");}} }

Job類邏輯,重要的地方已在注釋中說明。

@Component public class LeaveStartJob extends QuartzJobBean {private Scheduler scheduler;private SystemUserMapperPlus systemUserMapperPlus;@Autowiredpublic LeaveStartJob(Scheduler scheduler,SystemUserMapperPlus systemUserMapperPlus) {this.scheduler = scheduler;this.systemUserMapperPlus = systemUserMapperPlus;}@Overrideprotected void executeInternal(JobExecutionContext jobExecutionContext)throws JobExecutionException {Trigger trigger = jobExecutionContext.getTrigger();JobDetail jobDetail = jobExecutionContext.getJobDetail();JobDataMap jobDataMap = jobDetail.getJobDataMap();// 將添加任務(wù)的時候存進(jìn)去的數(shù)據(jù)拿出來long username = jobDataMap.getLongValue("username");LocalDateTime time = LocalDateTime.parse(jobDataMap.getString("time"));// 編寫任務(wù)的邏輯// 執(zhí)行之后刪除任務(wù)try {// 暫停觸發(fā)器的計時scheduler.pauseTrigger(trigger.getKey());// 移除觸發(fā)器中的任務(wù)scheduler.unscheduleJob(trigger.getKey());// 刪除任務(wù)scheduler.deleteJob(jobDetail.getKey());} catch (SchedulerException e) {e.printStackTrace();}} }

5 總結(jié)

上文所述的內(nèi)容應(yīng)該可以滿足絕大部分定時任務(wù)的需求。我在查閱網(wǎng)上的博客之后,發(fā)現(xiàn)大部分博客里介紹的Quartz使用還是停留在Spring階段,配置也都是通過xml,因此我在實現(xiàn)了功能以后,將整個過程總結(jié)了一下,留給需要的人以及以后的自己做參考。

總體上來說,Quartz實現(xiàn)定時任務(wù)還是非常方便的,與SpringBoot整合之后配置也非常簡單,是實現(xiàn)定時任務(wù)的不錯的選擇。

5.2 小坑1

在IDEA2020.1版本里使用SpringBoot與Quartz時,報錯找不到org.quartz程序包,但是依賴?yán)锩婷髅饔衞rg.quartz,類里的import也沒有報錯,還可以通過Ctrl+鼠標(biāo)左鍵直接跳轉(zhuǎn)到相應(yīng)的類里。后面我用了IDEA2019.3.4就不再有這個錯誤。那么就是新版IDEA的BUG了。

本文由博客群發(fā)一文多發(fā)等運營工具平臺 OpenWrite 發(fā)布
來源:https://www.tuicool.com/articles/zyQfmqV

總結(jié)

以上是生活随笔為你收集整理的c#quartz触发_SpringBoot集成Quartz实现定时任务的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。