最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot開(kāi)發(fā)實(shí)戰(zhàn)系列之動(dòng)態(tài)定時(shí)任務(wù)

 更新時(shí)間:2021年08月12日 14:26:23   作者:huanzi-qch  
在我們?nèi)粘5拈_(kāi)發(fā)中,很多時(shí)候,定時(shí)任務(wù)都不是寫(xiě)死的,而是寫(xiě)到數(shù)據(jù)庫(kù)中,從而實(shí)現(xiàn)定時(shí)任務(wù)的動(dòng)態(tài)配置,下面這篇文章主要給大家介紹了關(guān)于SpringBoot開(kāi)發(fā)實(shí)戰(zhàn)系列之動(dòng)態(tài)定時(shí)任務(wù)的相關(guān)資料,需要的朋友可以參考下

前言

定時(shí)器是我們項(xiàng)目中經(jīng)常會(huì)用到的,SpringBoot使用@Scheduled注解可以快速啟用一個(gè)簡(jiǎn)單的定時(shí)器(詳情請(qǐng)看我們之前的博客《SpringBoot系列——定時(shí)器》),然而這種方式的定時(shí)器缺乏靈活性,如果需要對(duì)定時(shí)器進(jìn)行調(diào)整,需要重啟項(xiàng)目才生效,本文記錄SpringBoot如何靈活配置動(dòng)態(tài)定時(shí)任務(wù)

代碼編寫(xiě)

首先先建表,重要字段:唯一表id、Runnable任務(wù)類(lèi)、Cron表達(dá)式,其他的都是一些額外補(bǔ)充字段

DROP TABLE IF EXISTS `tb_task`;
CREATE TABLE `tb_task`  (
  `task_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '定時(shí)任務(wù)id',
  `task_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '定時(shí)任務(wù)名稱(chēng)',
  `task_desc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '定時(shí)任務(wù)描述',
  `task_exp` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '定時(shí)任務(wù)Cron表達(dá)式',
  `task_status` int(1) NULL DEFAULT NULL COMMENT '定時(shí)任務(wù)狀態(tài),0停用 1啟用',
  `task_class` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '定時(shí)任務(wù)的Runnable任務(wù)類(lèi)完整路徑',
  `update_time` datetime NULL DEFAULT NULL COMMENT '更新時(shí)間',
  `create_time` datetime NULL DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',
  PRIMARY KEY (`task_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '動(dòng)態(tài)定時(shí)任務(wù)表' ROW_FORMAT = Compact;


INSERT INTO `tb_task` VALUES ('1', 'task1', '測(cè)試動(dòng)態(tài)定時(shí)任務(wù)1', '0/5 * * * * ?', 0, 'cn.huanzi.qch.springboottimer.task.MyRunnable1', '2021-08-06 17:39:23', '2021-08-06 17:39:25');
INSERT INTO `tb_task` VALUES ('2', 'task2', '測(cè)試動(dòng)態(tài)定時(shí)任務(wù)2', '0/5 * * * * ?', 0, 'cn.huanzi.qch.springboottimer.task.MyRunnable2', '2021-08-06 17:39:23', '2021-08-06 17:39:25');

項(xiàng)目引入jpa、數(shù)據(jù)庫(kù)驅(qū)動(dòng),用于數(shù)據(jù)庫(kù)操作

<!--添加springdata-jpa依賴(lài) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!--添加MySQL驅(qū)動(dòng)依賴(lài) -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

數(shù)據(jù)庫(kù)相關(guān)配置文件

spring:
    datasource: #數(shù)據(jù)庫(kù)相關(guān)
      url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&characterEncoding=utf-8
      username: root
      password: 123456
      driver-class-name: com.mysql.jdbc.Driver
    mvc:
      date-format: yyyy-MM-dd HH:mm:ss #mvc接收參數(shù)時(shí)對(duì)日期進(jìn)行格式化

    jackson:
      date-format: yyyy-MM-dd HH:mm:ss #jackson對(duì)響應(yīng)回去的日期參數(shù)進(jìn)行格式化
      time-zone: GMT+8
    jpa:
      show-sql: true

entity實(shí)體與數(shù)據(jù)表映射,以及與之對(duì)應(yīng)的repository

/**
 * 動(dòng)態(tài)定時(shí)任務(wù)表
 * 重要屬性:唯一表id、Runnable任務(wù)類(lèi)、Cron表達(dá)式,
 * 其他的都是一些額外補(bǔ)充說(shuō)明屬性
 */
@Entity
@Table(name = "tb_task")
@Data
public class TbTask {
    @Id
    private String taskId;//定時(shí)任務(wù)id
    private String taskName;//定時(shí)任務(wù)名稱(chēng)
    private String taskDesc;//定時(shí)任務(wù)描述
    private String taskExp;//定時(shí)任務(wù)Cron表達(dá)式
    private Integer taskStatus;//定時(shí)任務(wù)狀態(tài),0停用 1啟用
    private String taskClass;//定時(shí)任務(wù)的Runnable任務(wù)類(lèi)完整路徑
    private Date updateTime;//更新時(shí)間
    private Date createTime;//創(chuàng)建時(shí)間
}
/**
 * TbTask動(dòng)態(tài)定時(shí)任務(wù)Repository
 */
@Repository
public interface TbTaskRepository extends JpaRepository<TbTask,String>, JpaSpecificationExecutor<TbTask> {
}

測(cè)試動(dòng)態(tài)定時(shí)器的配置類(lèi),主要作用:初始化線程池任務(wù)調(diào)度、讀取/更新數(shù)據(jù)庫(kù)任務(wù)、啟動(dòng)/停止定時(shí)器等

/**
 * 測(cè)試定時(shí)器2-動(dòng)態(tài)定時(shí)器
 */
@Slf4j
@Component
public class TestScheduler2 {

    //數(shù)據(jù)庫(kù)的任務(wù)
    public static ConcurrentHashMap<String, TbTask> tasks = new ConcurrentHashMap<>(10);

    //正在運(yùn)行的任務(wù)
    public static ConcurrentHashMap<String,ScheduledFuture> runTasks = new ConcurrentHashMap<>(10);

    //線程池任務(wù)調(diào)度
    private ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();

    @Autowired
    private TbTaskRepository tbTaskRepository;

    /**
     * 初始化線程池任務(wù)調(diào)度
     */
    @Autowired
    public TestScheduler2(){
        this.threadPoolTaskScheduler.setPoolSize(10);
        this.threadPoolTaskScheduler.setThreadNamePrefix("task-thread-");
        this.threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        this.threadPoolTaskScheduler.initialize();
    }

    /**
     * 獲取所有數(shù)據(jù)庫(kù)里的定時(shí)任務(wù)
     */
    private void getAllTbTask(){
        //查詢(xún)所有,并put到tasks
        TestScheduler2.tasks.clear();
        List<TbTask> list = tbTaskRepository.findAll();
        list.forEach((task)-> TestScheduler2.tasks.put(task.getTaskId(),task));
    }

    /**
     * 根據(jù)定時(shí)任務(wù)id,啟動(dòng)定時(shí)任務(wù)
     */
    void start(String taskId){
        try {
            //如果為空,重新獲取
            if(TestScheduler2.tasks.size() <= 0){
                this.getAllTbTask();
            }
            TbTask tbTask = TestScheduler2.tasks.get(taskId);

            //獲取并實(shí)例化Runnable任務(wù)類(lèi)
            Class<?> clazz = Class.forName(tbTask.getTaskClass());
            Runnable runnable = (Runnable)clazz.newInstance();

            //Cron表達(dá)式
            CronTrigger cron = new CronTrigger(tbTask.getTaskExp());

            //執(zhí)行,并put到runTasks
            TestScheduler2.runTasks.put(taskId, Objects.requireNonNull(this.threadPoolTaskScheduler.schedule(runnable, cron)));

            this.updateTaskStatus(taskId,1);

            log.info("{},任務(wù)啟動(dòng)!",taskId);
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            log.error("{},任務(wù)啟動(dòng)失敗...",taskId);
            e.printStackTrace();
        }

    }

    /**
     * 根據(jù)定時(shí)任務(wù)id,停止定時(shí)任務(wù)
     */
    void stop(String taskId){
        TestScheduler2.runTasks.get(taskId).cancel(true);

        TestScheduler2.runTasks.remove(taskId);

        this.updateTaskStatus(taskId,0);

        log.info("{},任務(wù)停止...",taskId);
    }

    /**
     * 更新數(shù)據(jù)庫(kù)動(dòng)態(tài)定時(shí)任務(wù)狀態(tài)
     */
    private void updateTaskStatus(String taskId,int status){
        TbTask task = tbTaskRepository.getOne(taskId);
        task.setTaskStatus(status);
        task.setUpdateTime(new Date());
        tbTaskRepository.save(task);
    }
}

接下來(lái)就是編寫(xiě)測(cè)試接口、測(cè)試Runnable類(lèi)(3個(gè)Runnable類(lèi),這里就不貼那么多了,就貼個(gè)MyRunnable1)

/**
 * Runnable任務(wù)類(lèi)1
 */
@Slf4j
public class MyRunnable1 implements Runnable {
    @Override
    public void run() {
        log.info("MyRunnable1  {}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }
}

Controller接口

/**
 * 動(dòng)態(tài)定時(shí)任務(wù)Controller測(cè)試
 */
@RestController
@RequestMapping("/tbTask/")
public class TbTaskController {

    @Autowired
    private TestScheduler2 testScheduler2;

    @Autowired
    private TbTaskRepository tbTaskRepository;

    /**
     * 啟動(dòng)一個(gè)動(dòng)態(tài)定時(shí)任務(wù)
     * http://localhost:10085/tbTask/start/2
     */
    @RequestMapping("start/{taskId}")
    public String start(@PathVariable("taskId") String taskId){
        testScheduler2.start(taskId);
        return "操作成功";
    }

    /**
     * 停止一個(gè)動(dòng)態(tài)定時(shí)任務(wù)
     * http://localhost:10085/tbTask/stop/2
     */
    @RequestMapping("stop/{taskId}")
    public String stop(@PathVariable("taskId") String taskId){
        testScheduler2.stop(taskId);
        return "操作成功";
    }

    /**
     * 更新一個(gè)動(dòng)態(tài)定時(shí)任務(wù)
     * http://localhost:10085/tbTask/save?taskId=2&taskExp=0/2 * * * * ?&taskClass=cn.huanzi.qch.springboottimer.task.MyRunnable3
     */
    @RequestMapping("save")
    public String save(TbTask task) throws IllegalAccessException {
        //先更新表數(shù)據(jù)
        TbTask tbTask = tbTaskRepository.getOne(task.getTaskId());

        //null值忽略
        List<String> ignoreProperties = new ArrayList<>(7);

        //反射獲取Class的屬性(Field表示類(lèi)中的成員變量)
        for (Field field : task.getClass().getDeclaredFields()) {
            //獲取授權(quán)
            field.setAccessible(true);
            //屬性名稱(chēng)
            String fieldName = field.getName();
            //屬性的值
            Object fieldValue = field.get(task);

            //找出值為空的屬性,我們復(fù)制的時(shí)候不進(jìn)行賦值
            if(null == fieldValue){
                ignoreProperties.add(fieldName);
            }
        }

        //org.springframework.beans BeanUtils.copyProperties(A,B):A中的值付給B
        BeanUtils.copyProperties(task, tbTask,ignoreProperties.toArray(new String[0]));
        tbTaskRepository.save(tbTask);
        TestScheduler2.tasks.clear();

        //停止舊任務(wù)
        testScheduler2.stop(tbTask.getTaskId());

        //重新啟動(dòng)
        testScheduler2.start(tbTask.getTaskId());
        return "操作成功";
    }
}

效果演示  

啟動(dòng)

啟動(dòng)一個(gè)定時(shí)任務(wù),http://localhost:10085/tbTask/start/2

可以看到,id為2的定時(shí)任務(wù)已經(jīng)被啟動(dòng),corn表達(dá)式為5秒執(zhí)行一次,runnable任務(wù)為MyRunnable2

修改

修改一個(gè)定時(shí)任務(wù),http://localhost:10085/tbTask/save?taskId=2&taskExp=0/2 * * * * ?&taskClass=cn.huanzi.qch.springboottimer.task.MyRunnable3

調(diào)用修改后,數(shù)據(jù)庫(kù)信息被修改,id為2的舊任務(wù)被停止重新啟用新任務(wù),corn表達(dá)式為2秒執(zhí)行一次,runnable任務(wù)類(lèi)為MyRunnable3

停止

停止一個(gè)定時(shí)任務(wù),http://localhost:10085/tbTask/stop/2

id為2的定時(shí)任務(wù)被停止

后記

可以看到,配置動(dòng)態(tài)定時(shí)任務(wù)后,可以方便、實(shí)時(shí)的對(duì)定時(shí)任務(wù)進(jìn)行修改、調(diào)整,再也不用重啟項(xiàng)目啦

SpringBoot配置動(dòng)態(tài)定時(shí)任務(wù)暫時(shí)先記錄到這,后續(xù)再進(jìn)行補(bǔ)充

代碼開(kāi)源

代碼已經(jīng)開(kāi)源、托管到我的GitHub、碼云:

GitHub:https://github.com/huanzi-qch/springBoot

碼云:https://gitee.com/huanzi-qch/springBoot

到此這篇關(guān)于SpringBoot開(kāi)發(fā)實(shí)戰(zhàn)系列之動(dòng)態(tài)定時(shí)任務(wù)的文章就介紹到這了,更多相關(guān)SpringBoot動(dòng)態(tài)定時(shí)任務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MPAndroidChart開(kāi)源圖表庫(kù)的使用介紹之餅狀圖、折線圖和柱狀圖

    MPAndroidChart開(kāi)源圖表庫(kù)的使用介紹之餅狀圖、折線圖和柱狀圖

    這篇文章主要介紹了MPAndroidChart開(kāi)源圖表庫(kù)的使用介紹之餅狀圖、折線圖和柱狀圖的相關(guān)資料,需要的朋友可以參考下
    2016-02-02
  • Java中為什么重寫(xiě)equals()也需要重寫(xiě)hashCode方法

    Java中為什么重寫(xiě)equals()也需要重寫(xiě)hashCode方法

    這篇文章主要介紹了Java中為什么重寫(xiě)equals()也需要重寫(xiě)hashCode(),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • Java經(jīng)典算法匯總之順序查找(Sequential Search)

    Java經(jīng)典算法匯總之順序查找(Sequential Search)

    Java查找算法之順序查找說(shuō)明:順序查找適合于存儲(chǔ)結(jié)構(gòu)為順序存儲(chǔ)或鏈接存儲(chǔ)的線性表。 下面我們來(lái)詳細(xì)說(shuō)明下
    2016-04-04
  • Java的四種引用方式

    Java的四種引用方式

    這篇文章主要介紹了Java的四種引用方式,Java的引用方式主要包括強(qiáng)引用、軟引用、弱引用、虛引用;下面文章便來(lái)詳細(xì)介紹這四種引用方式,需要的朋友可以參考一下
    2021-10-10
  • 手把手教你用Java實(shí)現(xiàn)一套簡(jiǎn)單的鑒權(quán)服務(wù)

    手把手教你用Java實(shí)現(xiàn)一套簡(jiǎn)單的鑒權(quán)服務(wù)

    現(xiàn)今大部分系統(tǒng)都會(huì)有自己的鑒權(quán)服務(wù),本文介紹了最常用的鑒權(quán)服務(wù),就是日常用戶(hù)的登錄登出,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • maven一鍵刪除倉(cāng)庫(kù)無(wú)用文件的實(shí)現(xiàn)

    maven一鍵刪除倉(cāng)庫(kù)無(wú)用文件的實(shí)現(xiàn)

    大家都知道我們?cè)谑褂肕aven的時(shí)候,會(huì)下載一堆無(wú)用非jar文件,本文主要介紹了maven一鍵刪除倉(cāng)庫(kù)無(wú)用文件的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-11-11
  • JPA中@ElementCollection使用示例詳解

    JPA中@ElementCollection使用示例詳解

    在JPA中,@ElementCollection注解主要用于映射集合屬性,例如List、Set或數(shù)組等集合屬性,以及Map結(jié)構(gòu)的集合屬性,每個(gè)屬性值都有對(duì)應(yīng)的key映射,這篇文章主要介紹了JPA中@ElementCollection使用,需要的朋友可以參考下
    2023-11-11
  • Java實(shí)戰(zhàn)花店商城系統(tǒng)的實(shí)現(xiàn)流程

    Java實(shí)戰(zhàn)花店商城系統(tǒng)的實(shí)現(xiàn)流程

    只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+Springboot+Maven+mybatis+Vue+Mysql實(shí)現(xiàn)一個(gè)花店商城系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平
    2022-01-01
  • Mybatis-Plus中的MetaObjectHandler組件的使用

    Mybatis-Plus中的MetaObjectHandler組件的使用

    MetaObjectHandler是Mybatis-Plus中一個(gè)實(shí)用組件,專(zhuān)門(mén)用于自動(dòng)處理實(shí)體對(duì)象中的特定字段,如創(chuàng)建時(shí)間、更新時(shí)間、創(chuàng)建人和修改人等,該接口允許開(kāi)發(fā)者在不修改業(yè)務(wù)代碼的情況下,實(shí)現(xiàn)自動(dòng)填充功能,極大地簡(jiǎn)化了代碼的復(fù)雜性,感興趣的可以了解一下
    2024-10-10
  • @RequestBody的使用案例代碼

    @RequestBody的使用案例代碼

    @RequestBody主要用來(lái)接收前端傳遞給后端的json字符串中的數(shù)據(jù)的,這篇文章主要介紹了@RequestBody的使用,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-02-02

最新評(píng)論

台南市| 麻江县| 盐边县| 潢川县| 乌什县| 旬阳县| 志丹县| 图木舒克市| 电白县| 德江县| 桦甸市| 西林县| 商水县| 栾川县| 杨浦区| 蒲江县| 仪陇县| 东台市| 林周县| 阿拉善左旗| 越西县| 北碚区| 金昌市| 且末县| 台江县| 滁州市| 晋州市| 平昌县| 徐汇区| 海兴县| 古丈县| 泊头市| 凤庆县| 大理市| 安平县| 德昌县| 徐汇区| 岚皋县| 昭苏县| 洛隆县| 砀山县|