1. 程式人生 > 其它 >springboot專案之定時任務框架Quartz

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上分享,服務上線公網訪問。