最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot集成Quartz實(shí)現(xiàn)定時(shí)任務(wù)的方法

 更新時(shí)間:2020年05月06日 09:31:44   作者:民意代表  
Quartz是一個(gè)定時(shí)任務(wù)框架,其他介紹網(wǎng)上也很詳盡。這里要介紹一下Quartz里的幾個(gè)非常核心的接口。通過實(shí)例代碼給大家講解SpringBoot集成Quartz實(shí)現(xiàn)定時(shí)任務(wù)的方法,感興趣的朋友一起看看吧

1 需求

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

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

2 Quartz

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

2.1 Scheduler接口

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

2.2 Trigger接口

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

2.3 JobDetail接口

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

3 SpringBoot官方文檔解讀

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

現(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`這個(gè)啟動器。如果Quartz可用,Scheduler會通過SchedulerFactoryBean這個(gè)工廠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 建表

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

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

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

4.2 引入依賴

pom.xml里添加依賴。

<!-- quartz 定時(shí)任務(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é)束時(shí)會等待quartz相關(guān)的內(nèi)容結(jié)束
  wait-for-jobs-to-complete-on-shutdown: true
  # QuartzScheduler啟動時(shí)更新己存在的Job,這樣就不用每次修改targetObject后刪除qrtz_job_details表對應(yīng)記錄
  overwrite-existing-jobs: true
  # 這里居然是個(gè)map,搞得智能提示都沒有,佛了
  properties:
  org:
   quartz:
   	# scheduler相關(guān)
   scheduler:
    # scheduler的實(shí)例名
    instanceName: scheduler
    instanceId: AUTO
   # 持久化相關(guān)
   jobStore:
    class: org.quartz.impl.jdbcjobstore.JobStoreTX
    driverDelegateClass: 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: 5
    threadsInheritContextClassLoaderOfInitializingThread: true

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

第1節(jié)中提到的第一個(gè)子需求是在每天0點(diǎn)執(zhí)行的,是一個(gè)周期性的任務(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)入錯(cuò)誤
 */
@Configuration
public class QuartzConfig {

 @Bean
 public JobDetail jobDetail() {
  JobDetail jobDetail = JobBuilder.newJob(StartOfDayJob.class)
    .withIdentity("start_of_day", "start_of_day")
    .storeDurably()
    .build();
  return jobDetail;
 }

 @Bean
 public Trigger trigger() {
  Trigger trigger = TriggerBuilder.newTrigger()
    .forJob(jobDetail())
    .withIdentity("start_of_day", "start_of_day")
    .startNow()
    // 每天0點(diǎn)執(zhí)行
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0 0 * * ?"))
    .build();
  return trigger;
 }
}

builder類創(chuàng)建了一個(gè)JobDetail和一個(gè)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)識可以相同,因?yàn)樗麄兪遣煌念悺?/p>

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

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

@Component
public class StartOfDayJob extends QuartzJobBean {
 private StudentService studentService;

 @Autowired
 public StartOfDayJob(StudentService studentService) {
  this.studentService = studentService;
 }

 @Override
 protected void executeInternal(JobExecutionContext jobExecutionContext)
   throws JobExecutionException {
  // 任務(wù)的具體邏輯
 }
}

這里面有一個(gè)小問題,上面用builder創(chuàng)建JobDetail時(shí),傳入了StartOfDayJob.class,按常理推測,應(yīng)該是Quartz框架通過反射創(chuàng)建StartOfDayJob對象,再調(diào)用executeInternal()執(zhí)行任務(wù)。這樣依賴,這個(gè)Job是Quartz通過反射創(chuàng)建的,即使加了注解@Component,這個(gè)StartOfDayJob對象也不會被注冊到ioc容器中,更不可能實(shí)現(xiàn)依賴的自動裝配。

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

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

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

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

  • Job類需要獲取到一些數(shù)據(jù)用于任務(wù)的執(zhí)行;任務(wù)執(zhí)行完成后刪除Job和Trigger。
  • 業(yè)務(wù)邏輯是在老師批準(zhǔn)學(xué)生的請假申請時(shí),向調(diào)度器添加Trigger和JobDetail。

實(shí)體類:

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 {
 @Autowired
 private Scheduler scheduler;
 
 // 省略其他方法與其他依賴

 /**
  * 添加job和trigger到scheduler
  */
 private void addJobAndTrigger(LeaveApplication leaveApplication) {
  Long proposerUsername = leaveApplication.getProposerUsername();
  // 創(chuàng)建請假開始Job
  LocalDateTime startTime = leaveApplication.getStartTime();
  JobDetail startJobDetail = JobBuilder.newJob(LeaveStartJob.class)
   	// 指定任務(wù)組名和任務(wù)名
    .withIdentity(leaveApplication.getStartTime().toString(),
      proposerUsername + "_start")
    // 添加一些參數(shù),執(zhí)行的時(shí)候用
    .usingJobData("username", proposerUsername)
    .usingJobData("time", startTime.toString())
    .build();
  // 創(chuàng)建請假開始任務(wù)的觸發(fā)器
  // 創(chuàng)建cron表達(dá)式指定任務(wù)執(zhí)行的時(shí)間,由于請假時(shí)間是確定的,所以年月日時(shí)分秒都是確定的,這也符合任務(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;

 @Autowired
 public LeaveStartJob(Scheduler scheduler,
       SystemUserMapperPlus systemUserMapperPlus) {
  this.scheduler = scheduler;
  this.systemUserMapperPlus = systemUserMapperPlus;
 }

 @Override
 protected void executeInternal(JobExecutionContext jobExecutionContext)
   throws JobExecutionException {
  Trigger trigger = jobExecutionContext.getTrigger();
  JobDetail jobDetail = jobExecutionContext.getJobDetail();
  JobDataMap jobDataMap = jobDetail.getJobDataMap();
  // 將添加任務(wù)的時(shí)候存進(jìn)去的數(shù)據(jù)拿出來
  long username = jobDataMap.getLongValue("username");
  LocalDateTime time = LocalDateTime.parse(jobDataMap.getString("time"));

  // 編寫任務(wù)的邏輯

  // 執(zhí)行之后刪除任務(wù)
  try {
   // 暫停觸發(fā)器的計(jì)時(shí)
   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)該可以滿足絕大部分定時(shí)任務(wù)的需求。我在查閱網(wǎng)上的博客之后,發(fā)現(xiàn)大部分博客里介紹的Quartz使用還是停留在Spring階段,配置也都是通過xml,因此我在實(shí)現(xiàn)了功能以后,將整個(gè)過程總結(jié)了一下,留給需要的人以及以后的自己做參考。

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

5.2 小坑1

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

到此這篇關(guān)于SpringBoot集成Quartz實(shí)現(xiàn)定時(shí)任務(wù)的文章就介紹到這了,更多相關(guān)SpringBoot集成Quartz實(shí)現(xiàn)定時(shí)任務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一段眼睛跟著鼠標(biāo)轉(zhuǎn)動的跟蹤眼代碼

    一段眼睛跟著鼠標(biāo)轉(zhuǎn)動的跟蹤眼代碼

    java實(shí)現(xiàn)的眼睛跟著鼠標(biāo)轉(zhuǎn)動的跟蹤眼代碼
    2008-10-10
  • 關(guān)于Spring中一級緩存、二級緩存和三級緩存的那些事

    關(guān)于Spring中一級緩存、二級緩存和三級緩存的那些事

    Spring解決循環(huán)依賴的核心思想在于提前曝,下面這篇文章主要給大家介紹了關(guān)于Spring中一級緩存、二級緩存和三級緩存的那些事,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • 多個(gè)springboot項(xiàng)目如何使用一個(gè)外部共同的application.yml

    多個(gè)springboot項(xiàng)目如何使用一個(gè)外部共同的application.yml

    這篇文章主要介紹了多個(gè)springboot項(xiàng)目如何使用一個(gè)外部共同的application.yml問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • java自定義線程模型處理方法分享

    java自定義線程模型處理方法分享

    本文給大家總結(jié)分享了下個(gè)人關(guān)于java處理自定義線程模型的一些經(jīng)驗(yàn)和處理方法,有需要的小伙伴可以參考下
    2016-08-08
  • Java使用通配符實(shí)現(xiàn)增強(qiáng)泛型詳解

    Java使用通配符實(shí)現(xiàn)增強(qiáng)泛型詳解

    泛型是JAVA重要的特性,使用泛型編程,可以使代碼復(fù)用率提高。本文將利用通配符實(shí)現(xiàn)增強(qiáng)泛型,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-08-08
  • 關(guān)于JVM翻越內(nèi)存管理的墻

    關(guān)于JVM翻越內(nèi)存管理的墻

    這篇文章主要介紹了JVM翻越內(nèi)存管理的墻,由虛擬機(jī)管理內(nèi)存看起來一切都很美好,但也正是因?yàn)榘芽刂苾?nèi)存的權(quán)力交給了Java虛擬機(jī),一旦出現(xiàn)內(nèi)存泄漏和溢出方面的問題,就不得不從Java虛擬機(jī)角度上去排查問題,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-05-05
  • RocketMQ消息丟失場景以及解決方法

    RocketMQ消息丟失場景以及解決方法

    這篇文章主要給大家介紹了關(guān)于RocketMQ消息丟失場景以及解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • java模擬客戶端向服務(wù)器上傳文件

    java模擬客戶端向服務(wù)器上傳文件

    這篇文章主要為大家詳細(xì)介紹了java模擬客戶端向服務(wù)器上傳文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • SpringBoot + SpringSecurity 短信驗(yàn)證碼登錄功能實(shí)現(xiàn)

    SpringBoot + SpringSecurity 短信驗(yàn)證碼登錄功能實(shí)現(xiàn)

    這篇文章主要介紹了SpringBoot + SpringSecurity 短信驗(yàn)證碼登錄功能實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-06-06
  • java多線程學(xué)習(xí)之死鎖的模擬和避免(實(shí)例講解)

    java多線程學(xué)習(xí)之死鎖的模擬和避免(實(shí)例講解)

    下面小編就為大家?guī)硪黄猨ava多線程學(xué)習(xí)之死鎖的模擬和避免(實(shí)例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-06-06

最新評論

浪卡子县| 大名县| 忻州市| 嵩明县| 砀山县| 岱山县| 靖江市| 礼泉县| 彭山县| 建瓯市| 碌曲县| 万载县| 葫芦岛市| 石柱| 尤溪县| 若尔盖县| 靖远县| 农安县| 遂平县| 平湖市| 获嘉县| 广河县| 新津县| 眉山市| 德州市| 溧水县| 洪洞县| 嘉义市| 海南省| 静安区| 芷江| 凤城市| 吴忠市| 中卫市| 许昌县| 唐海县| 栾川县| 乐东| 周宁县| 交口县| 青浦区|