SpringBoot集成Quartz實現定時任務的動態創建、啓動、暫停、恢復、刪除。

點擊上方 "程序員小樂"關注, 星標或置頂一起成長

後臺回覆“大禮包”有驚喜禮包!

關注訂閱號「程序員小樂」,收看更多精彩內容

每日英文

When you are free from desire, you will be happy, because you will never be disappointed.

當你沒有慾望,你就會快樂,因爲你永不會失望。

每日掏心話

懂得進退,才能成就人生;懂得取捨,才能淡定從容;懂得知足,才能怡養心性;懂得刪減,才能輕鬆釋然;懂得變通,纔會少走彎路;懂得反思,纔會提高自己。

來自:毅大師 | 責編:樂樂

鏈接:blog.csdn.net/qq_39648029/article/details/108993476

 

後端架構師(ID:study_tech)第 1068 次推文

 

往日回顧:京東單方面辭退38歲 P7 員工,勞動仲裁京東三次敗訴,員工復崗三天又收解聘通知

 

    

   正文   

 

看了好多文章,都只講了基礎的demo用法,也就是簡單的創建運行定時任務,對定時任務的管理卻很少。

我這裏從0開始搭建一個簡單的demo,包括定時任務的各種操作,以及API的一些用法,可以實現大多場景的需求。如:

  1. 普通定時任務的創建、啓動、停止。

  2. 動態創建定時任務,如創建一個訂單,5分鐘後執行某某操作。

 

 

一、整個 Quartz 的代碼流程基本基本如下:

 

  1. 首先需要創建我們的任務(Job),比如取消訂單、定時發送短信郵件之類的,這是我們的任務主體,也是寫業務邏輯的地方。

  2. 創建任務調度器(Scheduler),這是用來調度任務的,主要用於啓動、停止、暫停、恢復等操作,也就是那幾個api的用法。

  3. 創建任務明細(JobDetail),最開始我們編寫好任務(Job)後,只是寫好業務代碼,並沒有觸發,這裏需要用JobDetail來和之前創建的任務(Job)關聯起來,便於執行。

  4. 創建觸發器(Trigger),觸發器是來定義任務的規則的,比如幾點執行,幾點結束,幾分鐘執行一次等等。這裏觸發器主要有兩大類(SimpleTrigger和CronTrigger)。

  5. 根據Scheduler來啓動JobDetail與Trigger

 

二、進入正題,引入依賴

 

  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-quartz</artifactId>
  </dependency>

 

三、創建Job

 

需實現Job接口,這個接口就一個execute()方法需要重寫,方法內容就是具體的業務邏輯。如果是動態任務呢,比如取消訂單,每次執行都是不同的訂單號。

這個時候就需要在創建任務(JobDetail)或者創建觸發器(Trigger)的那裏傳入參數,然後在這裏通過JobExecutionContext來獲取參數進行處理,

在公衆號程序員小樂後臺回覆“offer”,獲取算法面試題和答案。

import com.dy.utils.DateUtil;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
 
/**
 * @program: xiudo-ota
 * @description: 測試定時任務
 * @author: zhang yi
 * @create: 2020-10-09 14:38
 */
@DisallowConcurrentExecution//Job中的任務有可能併發執行,例如任務的執行時間過長,而每次觸發的時間間隔太短,則會導致任務會被併發執行。如果是併發執行,就需要一個數據庫鎖去避免一個數據被多次處理。
public class TestJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.err.println(jobExecutionContext.getJobDetail().getJobDataMap().get("name"));
        System.err.println(jobExecutionContext.getJobDetail().getJobDataMap().get("age"));
        System.err.println(jobExecutionContext.getTrigger().getJobDataMap().get("orderNo"));
        System.err.println("定時任務執行,當前時間:"+ DateUtil.formatDateTime(new Date()));
    }
}

 

四、創建任務調度器(Scheduler)

 

這裏採用Spring IOC,所以直接注入完事。如果是普通的,則需通過工廠創建。

工廠:

SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();

IOC:

@Autowired
private Scheduler scheduler;

 

五、創建任務明細(JobDetail)

 

/**通過JobBuilder.newJob()方法獲取到當前Job的具體實現(以下均爲鏈式調用)
 * 這裏是固定Job創建,所以代碼寫死XXX.class
 * 如果是動態的,根據不同的類來創建Job,則 ((Job)Class.forName("com.zy.job.TestJob").newInstance()).getClass()
 * 即是 JobBuilder.newJob(((Job)Class.forName("com.zy.job.TestJob").newInstance()).getClass())
 * */
JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
        /**給當前JobDetail添加參數,K V形式*/
        .usingJobData("name","zy")
        /**給當前JobDetail添加參數,K V形式,鏈式調用,可以傳入多個參數,在Job實現類中,可以通過jobExecutionContext.getJobDetail().getJobDataMap().get("age")獲取值*/
        .usingJobData("age",23)
        /**添加認證信息,有3種重寫的方法,我這裏是其中一種,可以查看源碼看其餘2種*/
        .withIdentity("我是name","我是group")
        .build();//執行

 

六、創建觸發器(Trigger)

 

這裏主要分爲兩大類SimpleTrigger、CronTrigger。

SimpleTrigger:是根據它自帶的api方法設置規則,比如每隔5秒執行一次、每隔1小時執行一次。

Trigger trigger = TriggerBuilder.newTrigger()
        /**給當前JobDetail添加參數,K V形式,鏈式調用,可以傳入多個參數,在Job實現類中,可以通過jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")獲取值*/
        .usingJobData("orderNo", "123456")
        /**添加認證信息,有3種重寫的方法,我這裏是其中一種,可以查看源碼看其餘2種*/
        .withIdentity("我是name","我是group")
        /**立即生效*/
//      .startNow()
        /**開始執行時間*/
        .startAt(start)
        /**結束執行時間,不寫永久執行*/
        .endAt(start)
        /**添加執行規則,SimpleTrigger、CronTrigger的區別主要就在這裏*/
        .withSchedule(
                SimpleScheduleBuilder.simpleSchedule()
                /**每隔3s執行一次,api方法有好多規則自行查看*/
                .withIntervalInSeconds(3)
                /**一直執行,如果不寫,定時任務就執行一次*/
                .repeatForever()
        )
        .build();//執行

CronTrigger:這就比較常用了,是基於Cron表達式來實現的。

CronTrigger  trigger = TriggerBuilder.newTrigger()
        /**給當前JobDetail添加參數,K V形式,鏈式調用,可以傳入多個參數,在Job實現類中,可以通過jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")獲取值*/
        .usingJobData("orderNo", "123456")
        /**添加認證信息,有3種重寫的方法,我這裏是其中一種,可以查看源碼看其餘2種*/
        .withIdentity("我是name","我是group")
        /**立即生效*/
//      .startNow()
        /**開始執行時間*/
        .startAt(start)
        /**結束執行時間,不寫永久執行*/
        .endAt(start)
        /**添加執行規則,SimpleTrigger、CronTrigger的區別主要就在這裏,我這裏是demo,寫了個每2分鐘執行一次*/
        .withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 * * * ?"))
        .build();//執行

注意:.startNow( )和.startAt( )這裏有個坑,這兩個方法是對同一個成員變量進行修改的 也就是說startAt和startNow同時調用的時候任務開始的時間是按後面調用的方法爲主的,誰寫在後面用誰。



 

七、啓動任務

 

/**添加定時任務*/
scheduler.scheduleJob(jobDetail, trigger);
if (!scheduler.isShutdown()) {
    /**啓動*/
    scheduler.start();
}

以上,任務的創建啓動都完事了,後面就是任務的暫停、恢復、刪除。比較簡單,大致原理就是我們在創建任務明細(JobDetail)和創建觸發器(Trigger)時,會調用.withIdentity(key,group)來傳入認證信息,後續就是根據這些認證信息來管理任務(通過api方法)

 

八、任務的暫停

 

scheduler.pauseTrigger(TriggerKey.triggerKey("我是剛纔寫的name","我是剛纔寫的group"));

 

九、任務的恢復

 

scheduler.resumeTrigger(TriggerKey.triggerKey("我是剛纔寫的name","我是剛纔寫的group"));

 

根據你寫的方式來獲取。

在公衆號程序員小樂後臺回覆“Java”,獲取Java面試題和答案。

 

十、任務的刪除

 

scheduler.pauseTrigger(TriggerKey.triggerKey("我是剛纔寫的name","我是剛纔寫的group"));//暫停觸發器
scheduler.unscheduleJob(TriggerKey.triggerKey("我是剛纔寫的name","我是剛纔寫的group"));//移除觸發器
scheduler.deleteJob(JobKey.jobKey("我是剛纔寫的name","我是剛纔寫的group"));//刪除Job

最後附上基本代碼,Job實現在上面:

    @Autowired
    private Scheduler scheduler;
 
    @PostMapping("/Quartz")
    @ApiOperation(value = "定時任務_創建", notes = "創建")
    @ResponseBody
    public Object quartz(@RequestParam("orderNo")  String orderNo) throws Exception {
        Date start=new Date(System.currentTimeMillis() + 7 * 1000);//當前時間7秒之後
 
        /**通過JobBuilder.newJob()方法獲取到當前Job的具體實現(以下均爲鏈式調用)
         * 這裏是固定Job創建,所以代碼寫死XXX.class
         * 如果是動態的,根據不同的類來創建Job,則 ((Job)Class.forName("com.zy.job.TestJob").newInstance()).getClass()
         * 即是 JobBuilder.newJob(((Job)Class.forName("com.zy.job.TestJob").newInstance()).getClass())
         * */
        JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
                /**給當前JobDetail添加參數,K V形式*/
                .usingJobData("name","zy")
                /**給當前JobDetail添加參數,K V形式,鏈式調用,可以傳入多個參數,在Job實現類中,可以通過jobExecutionContext.getJobDetail().getJobDataMap().get("age")獲取值*/
                .usingJobData("age",23)
                /**添加認證信息,有3種重寫的方法,我這裏是其中一種,可以查看源碼看其餘2種*/
                .withIdentity(orderNo)
                .build();//執行
 
 
        Trigger trigger = TriggerBuilder.newTrigger()
                /**給當前JobDetail添加參數,K V形式,鏈式調用,可以傳入多個參數,在Job實現類中,可以通過jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")獲取值*/
                .usingJobData("orderNo", orderNo)
                /**添加認證信息,有3種重寫的方法,我這裏是其中一種,可以查看源碼看其餘2種*/
                .withIdentity(orderNo)
                /**立即生效*/
//      .startNow()
                /**開始執行時間*/
                .startAt(start)
                /**結束執行時間*/
//        .endAt(start)
                /**添加執行規則,SimpleTrigger、CronTrigger的區別主要就在這裏*/
                .withSchedule(
                        SimpleScheduleBuilder.simpleSchedule()
                                /**每隔1s執行一次*/
                                .withIntervalInSeconds(3)
                                /**一直執行,*/
                                .repeatForever()
                )
                .build();//執行
 
//CronTrigger  trigger = TriggerBuilder.newTrigger()
//        /**給當前JobDetail添加參數,K V形式,鏈式調用,可以傳入多個參數,在Job實現類中,可以通過jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")獲取值*/
//        .usingJobData("orderNo", orderNo)
//        /**添加認證信息,有3種重寫的方法,我這裏是其中一種,可以查看源碼看其餘2種*/
//        .withIdentity(orderNo)
//        /**開始執行時間*/
//        .startAt(start)
//        /**結束執行時間*/
//        .endAt(start)
//        /**添加執行規則,SimpleTrigger、CronTrigger的區別主要就在這裏*/
//        .withSchedule(CronScheduleBuilder.cronSchedule("* 30 10 ? * 1/5 2018"))
//        .build();//執行
 
 
        /**添加定時任務*/
        scheduler.scheduleJob(jobDetail, trigger);
        if (!scheduler.isShutdown()) {
            /**啓動*/
            scheduler.start();
        }
        System.err.println("--------定時任務啓動成功 "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+" ------------");
        return "ok";
    }
 
    @PostMapping("/shutdown")
    @ApiOperation(value = "定時任務_停止", notes = "停止")
    @ResponseBody
    public Object shutdown(@RequestParam("orderNo")  String orderNo) throws IOException, SchedulerException {
        scheduler.pauseTrigger(TriggerKey.triggerKey(orderNo));//暫停Trigger
        return "";
    }
 
    @PostMapping("/resume")
    @ApiOperation(value = "定時任務_恢復", notes = "恢復")
    @ResponseBody
    public Object resume(@RequestParam("orderNo")  String orderNo) throws IOException, SchedulerException {
        scheduler.resumeTrigger(TriggerKey.triggerKey(orderNo));//恢復Trigger
        return "ok";
    }
 
    @PostMapping("/del")
    @ApiOperation(value = "定時任務_刪除", notes = "刪除")
    @ResponseBody
    public Object del(@RequestParam("orderNo")  String orderNo) throws IOException, SchedulerException {
        scheduler.pauseTrigger(TriggerKey.triggerKey(orderNo));//暫停觸發器
        scheduler.unscheduleJob(TriggerKey.triggerKey(orderNo));//移除觸發器
        scheduler.deleteJob(JobKey.jobKey(orderNo));//刪除Job
        return "ok";
    }

 

完事。。。。。。,如果想讓定時任務在啓動項目後自動啓動,則需要持久化任務,可以把基本信息保存在數據庫,項目啓動時啓動完,或者做分佈式任務。

 

PS:歡迎在留言區留    下你的觀點,一起討論提高。如果今天的文章讓你有新的啓發,歡迎轉發分享給更多人。

歡迎加入後端架構師交流羣,在後臺回覆“學習”即可。

猜你還想看

阿里、騰訊、百度、華爲、京東最新面試題彙集

任正非在榮耀送別會上的講話:一旦“離婚”就不要藕斷絲連,要做華爲全球最強的競爭對手

互聯網公司忽悠員工的黑話,套路太深了。。。

實戰:上億數據如何秒查

嘿,你在看嗎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章