四、springBoot 優雅的創建定時任務


前言

好幾天沒寫了,工作有點忙,最近工作剛好做一個定時任務統計的,所以就將springboot 如何創建定時任務整理了一下。
總的來說,springboot創建定時任務是非常簡單的,不用像spring 或者springmvc 需要在xml 文件中配置,在項目啟動的時候加載。spring boot 使用注解的方式就可以完全支持定時任務。
不過基礎注解的話,可能有的需求定時任務的時間會經常變動,注解就不好修改,每次都得重新編譯,所以想將定時時間存在數據庫,然后項目讀取數據庫執行定時任務,所以就有了基於接口的定時任務。下面就分基於注解和基於接口詳細講解。

基於注解

pom.xml 文件不用修改,我們原本的項目就支持,其實定時器是springboot框架自帶的,不用引入什么依賴。我們直接創建一個autotask 包,創建一個AutoTask類。

@EnableScheduling
@Component
@Slf4j
public class AutoTask {
    @Scheduled(cron="*/6 * * * * ?")
    private void process(){
        log.info("autoTask ");
    }
}

這樣一個定時器就創建啦,在項目啟動后,會每隔6s 打印“autoTask”的日志。是不是很簡單。主要用到的兩個注解就是@EnableScheduling 和 @Scheduled。
注解@EnableScheduling 就是開啟定時任務的。哪個類的中的方法想要定期執行,就在這個類上加入這個注解。當然這個這個注解也可以加在啟動類上。加在啟動類上表示項目中所有的類都可以創建定時任務。
file

@Scheduled 注解就是我們常見的定時器啦,后面的cron 就是定時任務表達式。在方法上注解,表示這個方法定期執行。
file
不過@Scheduled 可以進行兩種配置,我們熟悉的cron ,還有一種是fixedRate。比如fixedRate=6000 表示方法每6秒鍾執行一次。
我們來啟動項目看一下,可以看到兩個方法都在定期執行。
file

基於接口

上面可以看到springboot 基於注解是非常方便的。但是對於頻繁變動或者一個項目中有很多的定時器那就不方便管理了。所以統一將定時器信息存放在數據庫中。


DROP TABLE IF EXISTS `scheduled`;
CREATE TABLE `scheduled`  (
  `cron_id` varchar(30) NOT NULL PRIMARY KEY,
  `cron_name` varchar(30) NULL,
  `cron` varchar(30) NOT NULL
);
INSERT INTO `scheduled` VALUES ('1','定時器任務一','0/6 * * * * ?');

file

在dao 層mapper1包下創建一個CronMapper接口,很簡單的就獲取cron

public interface CronMapper {

    @Select("select cron from scheduled where cron_id = #{id}")
    public String getCron(int id);
}

file

這里我們就不寫service 層了。直接在autotask 包下創建一個AutoTaskFromDB類

@Slf4j
@Component
public class AutoTaskFromDB implements SchedulingConfigurer {

    @Autowired
    protected CronMapper cronMapper;

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {

        scheduledTaskRegistrar.addTriggerTask(() -> process(),
                triggerContext -> {
                    String cron = cronMapper.getCron(1);
                    if (cron.isEmpty()) {
                       log.info("cron 為空");
                    }
                    return new CronTrigger(cron).nextExecutionTime(triggerContext);
                }
        );
    }

    private  void process(){
        log.info("formDB ");
    }
}

可以看到也很簡單,就是實現SchedulingConfigurer 這個吧接口,addTriggerTask()是添加一個定時器。
process()方法是我們需要定時執行的方法體。
CronTrigger(cron).nextExecutionTime(triggerContext) 就是從數據庫讀取的cron 創建定時器。

這個類我沒有加上@EnableScheduling 注解,因為我在啟動類上加上了,如果你們啟動類上沒有加,這里記得加上。

測試一下;下圖可以看到三個定時任務都執行了,fromDB 是從數據庫讀取的。
file

cron

cron 用法網上有很多,也沒有什么講的這里就附帶記錄下

結構

cron表達式是一個字符串,分為6或7個域,每兩個域之間用空格分隔,
其語法格式為:"秒域 分域 時域 日域 月域 周域 年域"

取值范圍

域名 可取值 可取符號(僅列部分常用)
秒域 0~59的整數 * - , /
分域 0~59的整數 * - , /
時域 0~23的整數 * - , /
日域 1~31的整數 * - , / ? L
月域 112的整數或JANDEC * - , /
周域 17的整數或SUNSAT * - , / ? L #
年域 1970~2099的整數 * - , /

常例

表達式 意義
每隔5秒鍾執行一次 */5 * * * * ?
每隔1分鍾執行一次 0 * /1 * * * ?
每天1點執行一次 0 0 1 * * ?
每天23點55分執行一次 0 55 23 * * ?
每月最后一天23點執行一次 0 0 23 L * ?
每周六8點執行一次 0 0 8 ? * L
每月最后一個周五,每隔2小時執行一次 0 0 */2 ? * 6L
每月的第三個星期五上午10:15執行一次 0 15 10 ? * 5#3
在每天下午2點到下午2:05期間的每1分鍾執行 0 0-5 14 * * ?
表示周一到周五每天上午10:15執行 0 15 10 ? * 2-6
每個月的最后一個星期五上午10:15執行 0 15 10 ? * 6L
每天上午10點,下午2點,4點執行一次 0 0 10,14,16 * * ?
朝九晚五工作時間內每半小時執行一次 0 0/30 9-17 * * ?
每個星期三中午12點執行一次 0 0 12 ? * 4
每年三月的星期三的下午2:10和2:44各執行一次 0 10,44 14 ? 3 4
每月的第三個星期五上午10:15執行一次 0 15 10 ? * 6#3
每月一日凌晨2點30執行一次 0 30 2 1 * ?
每分鍾的第10秒與第20秒都會執行 10,20 * * * * ?
每月的第2個星期的周5,凌晨執行 0 0 0 ? * 6#2

番外

本來這個知識點不應該放在這里講的,但是不多,順帶寫了,剛好也能做定時器。我們項目中往往有一些需求需要在項目啟動的時候就執行,那這個我們怎么實現了。其實spring boot 使用起來也非常簡單,只用實現 ApplicationRunner 就好了。
我們在autotask 包下創建一個AutoTaskFromSpringRunner類

@Slf4j
@Component
public class AutoTaskFromSpringRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        process();
    }

    private void process(){
        log.info(" run ApplicationArguments");
    }
}

啟動項目看一下,可以發現這個會在項目啟動后執行,但是只會執行一次。

file
那這個怎么用來做定時器呢?當然是結合線程來做啦,但是這個方法其實不建議,b畢竟線程很容易出問題,但是提供一種思路:

@Slf4j
@Component
public class AutoTaskFromSpringRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        process();
        new Thread(() -> {
            while (true) {
                process2();
                try {
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    log.error("{}",e);
                }
            }
        }).start();
    }
    private void process(){
        log.info(" run ApplicationArguments");
    }
    private void process2(){
        log.info("線程定時器");
    }
}

啟動項目看下,發現也是可以起到定時器的作用的。
file

好了,就說這么多啦,今天項目的代碼也同步到github 上啦。
github地址:https://github.com/QuellanAn/zlflovemm

后續加油♡

歡迎大家關注個人公眾號 "程序員愛酸奶"

分享各種學習資料,包含java,linux,大數據等。資料包含視頻文檔以及源碼,同時分享本人及投遞的優質技術博文。

如果大家喜歡記得關注和分享喲❤
file


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM