1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關咨詢
      選擇下列產品馬上在線溝通
      服務時間:8:30-17:00
      你可能遇到了下面的問題
      關閉右側工具欄

      新聞中心

      這里有您想知道的互聯網營銷解決方案
      SpringBoot集成Quartz如何實現定時任務

      這篇文章主要為大家展示了SpringBoot集成Quartz如何實現定時任務,內容簡而易懂,希望大家可以學習一下,學習完之后肯定會有收獲的,下面讓小編帶大家一起來看看吧。

      我們提供的服務有:成都做網站、網站建設、微信公眾號開發(fā)、網站優(yōu)化、網站認證、巨野ssl等。為近1000家企事業(yè)單位解決了網站和推廣的問題。提供周到的售前咨詢和貼心的售后服務,是有科學管理、有技術的巨野網站制作公司

      1 需求

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

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

      2 Quartz

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

      2.1 Scheduler接口

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

      2.2 Trigger接口

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

      2.3 JobDetail接口

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

      3 SpringBoot官方文檔解讀

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

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

      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會被自動采集并關聯到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。不知道是不是我的使用方式有誤。后來仔細一想,文檔的意思應該是在創(chuàng)建Job對象之后,調用set方法將依賴注入進去。但后面我們是通過框架反射生成的Job對象,這樣做反而會搞得更加復雜。最后還是決定采用給Job類加@Component注解的方法。

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

      4 SpringBoot集成Quartz

      4.1 建表

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

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

      SpringBoot集成Quartz如何實現定時任務

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

      4.2 引入依賴

      pom.xml里添加依賴。

      
      
       org.springframework.boot
       spring-boot-starter-quartz
       2.2.6.RELEASE
      

      4.3 配置quartz

      application.yml中配置quartz。相關配置的作用已經寫在注解上。

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

      4.4 注冊周期性的定時任務

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

      /**
       * Quartz的相關配置,注冊JobDetail和Trigger
       * 注意JobDetail和Trigger是org.quartz包下的,不是spring包下的,不要導入錯誤
       */
      @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點執(zhí)行
          .withSchedule(CronScheduleBuilder.cronSchedule("0 0 0 * * ?"))
          .build();
        return trigger;
       }
      }

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

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

      JobDetail里有一個StartOfDayJob類,這個類就是Job接口的一個實現類,里面定義了任務的具體內容,看一下代碼:

      @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 {
        // 任務的具體邏輯
       }
      }

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

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

      SpringBoot集成Quartz如何實現定時任務

      SpringBoot集成Quartz如何實現定時任務

      4.5 注冊無周期性的定時任務

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

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

      • Job類需要獲取到一些數據用于任務的執(zhí)行;任務執(zhí)行完成后刪除Job和Trigger。
      • 業(yè)務邏輯是在老師批準學生的請假申請時,向調度器添加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 {
       @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)
         	// 指定任務組名和任務名
          .withIdentity(leaveApplication.getStartTime().toString(),
            proposerUsername + "_start")
          // 添加一些參數,執(zhí)行的時候用
          .usingJobData("username", proposerUsername)
          .usingJobData("time", startTime.toString())
          .build();
        // 創(chuàng)建請假開始任務的觸發(fā)器
        // 創(chuàng)建cron表達式指定任務執(zhí)行的時間,由于請假時間是確定的,所以年月日時分秒都是確定的,這也符合任務只執(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("添加請假任務失敗");
        }
       }
      }

      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();
        // 將添加任務的時候存進去的數據拿出來
        long username = jobDataMap.getLongValue("username");
        LocalDateTime time = LocalDateTime.parse(jobDataMap.getString("time"));
      
        // 編寫任務的邏輯
      
        // 執(zhí)行之后刪除任務
        try {
         // 暫停觸發(fā)器的計時
         scheduler.pauseTrigger(trigger.getKey());
         // 移除觸發(fā)器中的任務
         scheduler.unscheduleJob(trigger.getKey());
         // 刪除任務
         scheduler.deleteJob(jobDetail.getKey());
        } catch (SchedulerException e) {
         e.printStackTrace();
        }
       }
      }

      5 總結

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

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

      5.2 小坑1

      在IDEA2020.1版本里使用SpringBoot與Quartz時,報錯找不到org.quartz程序包,但是依賴里面明明有org.quartz,類里的import也沒有報錯,還可以通過Ctrl+鼠標左鍵直接跳轉到相應的類里。后面我用了IDEA2019.3.4就不再有這個錯誤。那么就是新版IDEA的BUG了。

      SpringBoot集成Quartz如何實現定時任務

      以上就是關于SpringBoot集成Quartz如何實現定時任務的內容,如果你們有學習到知識或者技能,可以把它分享出去讓更多的人看到。


      文章標題:SpringBoot集成Quartz如何實現定時任務
      標題路徑:http://ef60e0e.cn/article/gegigs.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        拜城县| 哈密市| 甘泉县| 忻城县| 金秀| 德安县| 呼玛县| 苗栗县| 原阳县| 河津市| 台东市| 什邡市| 游戏| 新田县| 平阴县| 广丰县| 顺义区| 新竹市| 杭锦后旗| 永清县| 同仁县| 清水河县| 布尔津县| 大丰市| 固始县| 绵竹市| 印江| 全州县| 丹巴县| 盈江县| 普宁市| 云梦县| 宝坻区| 中卫市| 襄城县| 称多县| 甘孜县| 桐梓县| 衡水市| 尉犁县| 故城县|