springboot專案之定時任務框架Quartz
技術標籤:javaspringquartzspring boot
在web專案中,我們經常會遇到一些需要定時執行的任務,比如定時從某個伺服器上下載檔案、定時刪除伺服器上的某些檔案、定時傳送一些訊息等等的操作,都需要定時任務。這裡在springboot專案中使用到的一個定時任務的框架Quartz。這也是我在專案中使用到的定時任務框架,下面對該框架做一個簡單的使用簡介。
簡而言之,Quartz是一種任務排程計劃,它是由OpenSymphony提供的、開源的、java編寫的強大任務排程框架。不管是小型專案,還是大型專案,叢集專案,Quartz都可以完美地解決其中的任務排程計劃問題。
springboot整合Quartz
首先在pom檔案中新增Quartz的starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
Quartz關鍵概念
Quartz主要有兩個核心元件,一個是Job,另一個是Trigger。Job表示要執行的任務是什麼,也就是要被排程的任務。Trigger表示什麼時候觸發該任務。在這裡不去過多追究原始碼,我們先把框架用起來再說。那麼在程式碼中怎麼實現呢,下面我以一個定時下載的任務為例說明Quartz的使用。我們先建立兩個核心元件的類,一個是關於Job的,一個是關於Trigger的。
建立任務 Job
我在專案下建立任務的包,在包下建立Job類。
這裡我建立EphDWJob類,用來定時從伺服器上下載檔案。
package meicius.ori.quartzJobs; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @Service public class EphDWJob implements Job { Logger logger = LoggerFactory.getLogger(EphDWJob.class); String ephDownLoadUrlPrefix; /** * 要下載的檔名 * **/ String ephFileName; @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { this.executeMain(); } public void executeMain(){ try { FtpDownload.downLoadFtpFile(ephDownLoadUrlPrefix, 21, "anonymous", ""); } catch (Exception e) { e.printStackTrace(); } } }
我們建立的Job要實現Quartz中Job介面,該介面中只有一個execute()方法,就是在這函式中執行我們的任務。
建立 Trigger
為了區分,我在專案下建立關於Trigger的包,在包下建立Trigger類。
接下來在該包下建立Trigger類。在這裡完成任務排程建立和啟動任務排程。我在類的建構函式中完成任務排程的建立,把任務排程的建立和任務排程的啟動分開。
package meicius.ori.quartzScheduler;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
public class EphDWScheduler {
Logger logger = LoggerFactory.getLogger(EphDWScheduler.class);
public SchedulerFactory schedulerFactory;
public Scheduler scheduler;
public JobDetail jobDetail;
public CronTrigger cronTrigger;
public TriggerKey triggerKey;
public JobKey jobKey;
/**
* 在建構函式中建立排程器,載入任務
* **/
public EphDWScheduler() throws SchedulerException {
//1 建立排程器
this.schedulerFactory = new StdSchedulerFactory();
this.scheduler = schedulerFactory.getScheduler();
//2 建立JobDetail例項,並與PrintWordsJob類繫結(Job執行的內容)
this.jobDetail = JobBuilder.newJob(EphDWJob.class)
.withIdentity("ephJobDetailDataDownLoadIdentity", "ephJobDetailDataDownLoadIdentityGroup1")
.usingJobData("ephJobDetailDataDownLoadJobData", "ephJobDetailDataDownLoadValue")
.build();
//3 構建觸發trigger例項
/**
* corn 表示式
* **/
this.cronTrigger = TriggerBuilder.newTrigger()
.withIdentity("ephTriggerDownLoadIdentity", "ephTriggerDownLoadIdentityGroup1")
.usingJobData("ephTriggerDownLoadJobData","ephTriggerDownLoadDataValue")
.withSchedule(CronScheduleBuilder.cronSchedule("0 15 */2 * * ?")) //每隔兩個小時的第15分鐘執行一次
.build();
this.triggerKey = TriggerKey.triggerKey("ephTriggerDownLoadIdentity", "ephTriggerDownLoadIdentityGroup1");
this.jobKey = JobKey.jobKey("ephJobDetailDataDownLoadIdentity", "ephJobDetailDataDownLoadIdentityGroup1");
}
/**
* 排程執行
* **/
public String startJob(JobDetail jobDetail, CronTrigger cronTrigger) throws SchedulerException {
try{
Date date = scheduler.scheduleJob(jobDetail, cronTrigger);
scheduler.start();
return "下載任務啟動成功";
}catch (Exception e){
return "下載任務啟動失敗" + e.getMessage();
}
}
}
這裡需要做的是
1)先建立任務排程器工廠schedulerFactory,然後再建立任務排程器scheduler。
2)建立任務詳情JobDetail,將要被排程的任務新增進來。這裡定義了在Quartz中任務所在的組名稱和任務名稱,供接下來排程使用。
3)建立trigger例項,也就是如何去排程,比如多久排程一次,什麼時候開始排程等等。這裡需要編寫corn 表示式,就是用這個表示式來表明如何排程,Quartz提供了完美的表示式書寫規則,可以滿足任何的排程規則。這裡寫幾個我用到的排程規則
每個小時的第0,5,10,15,20,25,30,35,40,45,50,55分鐘的第0秒執行
(CronScheduleBuilder.cronSchedule("0 0,5,10,15,20,25,30,35,40,45,50,55 * * * ?"))
每10分鐘執行一次
(CronScheduleBuilder.cronSchedule("* */10 * * * ?"))
每3秒鐘執行一次
(CronScheduleBuilder.cronSchedule("*/3 * * * * ?"))
每隔兩個小時的第15分鐘執行一次
(CronScheduleBuilder.cronSchedule("0 15 */2 * * ?"))
每隔1分鐘的第10秒鐘執行一次
(CronScheduleBuilder.cronSchedule("10 */1 * * * ?"))
每3秒鐘執行一次
(CronScheduleBuilder.cronSchedule("*/3 * * * * ?"))
4)啟動排程任務,根據任務所在組和任務名稱
至此,就完成了一個Quartz的簡單任務排程。
值得一說的是,在Quartz的Job中是無法注入Spring容器中的類的。這是因為Job是在Quartz框架中,Job實現類不接受Spring容器的管理。這裡有兩種方法解決此問題,一種是使用Quartz提供的JobFactory介面,就可以自定義實現建立Job的邏輯,並將jobFactory交給spring容器管理。另一種是直接從Spring容器中根據類或者類名查詢要使用的類。我在專案中就使用的是第二種方法。
Spring容器中bean的獲取
建立一個類,用來獲取容器中的類。
package meicius.ori.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextUtil.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext(){
return applicationContext;
}
public static Object getBean(String beanName){
return applicationContext.getBean(beanName);
};
//通過class獲取Bean.
public static <T> T getBeanByClass(Class<T> clazz){
return applicationContext.getBean(clazz);
}
}
這樣就可以完美地在Spring中使用Quartz了。
------------
關注我,持續為您推薦在springboot專案中使用到的技術,遇到的問題。此外還會推薦前端Vue專案、分享整個前後端專案的搭建,完成過程中使用到的技術、遇到的問題。本專案要做一個衛星綜合顯示平臺,後續會詳細介紹本次專案,同時也會在git上分享,服務上線公網訪問。