欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot?ScheduledTaskRegistrar解決動(dòng)態(tài)定時(shí)任務(wù)思路詳解

 更新時(shí)間:2023年02月01日 09:28:07   作者:Apluemxa  
本文將從問(wèn)題出發(fā),詳細(xì)介紹ScheduledTaskRegistrar類(lèi)是如何解決動(dòng)態(tài)調(diào)整定時(shí)任務(wù)的思路,并給出關(guān)鍵的代碼示例,幫助大家快速地上手學(xué)習(xí)

前言

在做SpringBoot項(xiàng)目的過(guò)程中,有時(shí)客戶(hù)會(huì)提出按照指定時(shí)間執(zhí)行一次業(yè)務(wù)的需求。

如果客戶(hù)需要改動(dòng)業(yè)務(wù)的執(zhí)行時(shí)間,即動(dòng)態(tài)地調(diào)整定時(shí)任務(wù)的執(zhí)行時(shí)間,那么可以采用SpringBoot自帶的ScheduledTaskRegistrar類(lèi)作為解決方案來(lái)實(shí)現(xiàn)。

在單一使用ScheduledTaskRegistrar類(lèi)解決定時(shí)任務(wù)問(wèn)題的時(shí)候,可能會(huì)達(dá)不到預(yù)期的動(dòng)態(tài)調(diào)整定時(shí)任務(wù)的效果。

如果靈活配合使用對(duì)應(yīng)的工具類(lèi)(ThreadPoolTaskScheduler類(lèi)),則可以方便地對(duì)動(dòng)態(tài)調(diào)整定時(shí)任務(wù)進(jìn)行管理。

本文會(huì)從問(wèn)題出發(fā),詳細(xì)介紹ScheduledTaskRegistrar類(lèi)是如何解決動(dòng)態(tài)調(diào)整定時(shí)任務(wù)的思路,并給出關(guān)鍵的代碼示例,幫助大家快速地上手學(xué)習(xí)。

一、問(wèn)題背景

在指定的某一時(shí)刻執(zhí)行業(yè)務(wù);

可以手動(dòng)地更改執(zhí)行時(shí)間。

在實(shí)際項(xiàng)目中,很少會(huì)有傻瓜式地去指定某一時(shí)間就觸發(fā)某個(gè)業(yè)務(wù)的場(chǎng)景,執(zhí)行業(yè)務(wù)的時(shí)間不是一成不變的,而是動(dòng)態(tài)地隨著客戶(hù)所指定的時(shí)間進(jìn)行調(diào)整的。

二、痛點(diǎn)所在

如果單一地使用SpringBoot自帶的ScheduledTaskRegistrar去實(shí)現(xiàn),那么可能會(huì)有以下問(wèn)題:

  • 只能按照指定的時(shí)間去執(zhí)行,更改執(zhí)行時(shí)間需要重啟服務(wù);
  • 無(wú)法刪除該定時(shí)任務(wù),或者刪除后無(wú)法再啟動(dòng)新的定時(shí)任務(wù)。
  • 業(yè)務(wù)邏輯與觸發(fā)器的代碼耦合度太高,無(wú)法將業(yè)務(wù)代碼從ScheduledTaskRegistrar類(lèi)中抽離出去。
/**
 * @author Created by zhuzqc on 2023/1/30 15:28
 */
@Slf4j
@Component
@EnableScheduling
public class ScheduleTaskDemo implements SchedulingConfigurer {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {

        //Runnable線(xiàn)程注冊(cè)任務(wù)
        Runnable taskOne = () -> {
            //需要執(zhí)行的業(yè)務(wù)邏輯,一般會(huì)在這里封裝好
            logger.info("----------業(yè)務(wù)執(zhí)行結(jié)束----------");
        };
        //任務(wù)的觸發(fā)時(shí)間,一般使用 cron 表達(dá)式
        Trigger triggerOne = triggerContext -> {
            Date nextExecTime = null;
            try {
                // 此處指定 cron 表達(dá)式
                String cron = "0 00 12 ? * *";
                if (StringUtils.isBlank(cron)) {
                    // 提示參數(shù)為空
                    logger.info("trigger定時(shí)器的 cron 參數(shù)為空!");
                    // 如果為空則賦默認(rèn)值,每天中午12點(diǎn)
                    cron = "0 00 12 ? * *";
                }
                logger.info("---------->定時(shí)任務(wù)執(zhí)行中<---------");
                CronTrigger cronTrigger = new CronTrigger(cron);
                nextExecTime = cronTrigger.nextExecutionTime(triggerContext);
            } catch (Exception e) {
                e.printStackTrace();
                log.info(e.getMessage());
            }
            return nextExecTime;
        };
        taskRegistrar.addTriggerTask(taskOne, triggerOne);
    }
}

上述代碼只能實(shí)現(xiàn)在指定的時(shí)間去觸發(fā)定時(shí)任務(wù),無(wú)法對(duì) cron 表達(dá)式進(jìn)行更改,如果更改則需要重新啟動(dòng)服務(wù),非常地“傻瓜”。

而在實(shí)際的編碼過(guò)程中,業(yè)務(wù)邏輯代碼需要單獨(dú)地剝離開(kāi)(解耦),如何做到業(yè)務(wù)邏輯代碼和觸發(fā)器代碼都能訪問(wèn)到外部業(yè)務(wù)數(shù)據(jù),是設(shè)計(jì)過(guò)程中需要考慮到的關(guān)鍵。

三、解決思路

  • TODO:如果要在此處將業(yè)務(wù)邏輯和時(shí)間觸發(fā)器進(jìn)行捆綁,那么在這個(gè)實(shí)現(xiàn)類(lèi)中無(wú)法獲取到來(lái)自該類(lèi)外部的業(yè)務(wù)數(shù)據(jù);
  • TODO:要解決這樣的問(wèn)題,就要找到一個(gè)辦法:既能將兩者抽離,又能實(shí)現(xiàn)靈活觸發(fā)定時(shí)任務(wù)。

在這里介紹一個(gè)名為T(mén)hreadPoolTaskScheduler類(lèi),通過(guò)源碼得知,該類(lèi)實(shí)現(xiàn)了SchedulingTaskExecutor和TaskScheduler接口。

該類(lèi)中schedule(Runnable task, Trigger trigger)方法,通過(guò)分別傳入線(xiàn)程任務(wù)(業(yè)務(wù)邏輯)Trigger觸發(fā)器對(duì)象作為參數(shù),支持動(dòng)態(tài)創(chuàng)建指定 cron 表達(dá)式的定時(shí)任務(wù)。

該方法源碼如下:

 	@Override
	@Nullable
	public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
		ScheduledExecutorService executor = getScheduledExecutor();
		try {
			ErrorHandler errorHandler = this.errorHandler;
			if (errorHandler == null) {
				errorHandler = TaskUtils.getDefaultErrorHandler(true);
			}
			return new ReschedulingRunnable(task, trigger, this.clock, executor, errorHandler).schedule();
		}
		catch (RejectedExecutionException ex) {
			throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
		}
	}

以下部分是對(duì)該方法的具體使用,核心思路如下 :

  • 實(shí)例化ThreadPoolTaskScheduler類(lèi)對(duì)象;
  • 實(shí)例化ScheduledFuture類(lèi)對(duì)象,用于初始化調(diào)用schedule()后的值。
  • 將攜帶有Runnable和Trigger的ScheduledFuture類(lèi)對(duì)象作為Map的value進(jìn)行裝配。
  • 根據(jù)Map的key對(duì)定時(shí)任務(wù)進(jìn)行管理,達(dá)到添加和刪除的目的。

四、代碼示例

代碼示例分為兩部分:

第一部分是關(guān)于ThreadPoolTaskScheduler類(lèi)和schedule()方法的使用;

/**
 * @author @author Created by zhuzqc on 2023/1/30 15:39
 * 任務(wù)線(xiàn)程池管理工具
 */
public class TaskSchedulerUtil {

    private static final Logger logger = LoggerFactory.getLogger(TaskSchedulerUtil.class);

    /**
     * 線(xiàn)程調(diào)度工具對(duì)象,作為該類(lèi)的成員變量
     */
    private static ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();

    /**
     *初始化 map 對(duì)象,裝配 schedule 方法的返回對(duì)象為 value 值
     */
    private static Map<String, ScheduledFuture<?>> scheduledFutureMap = new HashMap<String, ScheduledFuture<?>>();

    static {
        threadPoolTaskScheduler.initialize();
    }

    /**
     * 將Runnable對(duì)象和Trigger對(duì)象作為參數(shù)傳入該靜態(tài)方法
     * @param runnable
     * @param trigger
     * @param 定時(shí)任務(wù)id
     */
    public static void put(Runnable runnable, Trigger trigger, String id) {
        // 將攜帶有Runnable和Trigger的ScheduledFuture類(lèi)對(duì)象作為 Map 的 value 進(jìn)行裝配
        ScheduledFuture<?> scheduledFuture = threadPoolTaskScheduler.schedule(runnable, trigger);
        // 放入 Map 中作為 value
        scheduledFutureMap.put(id, scheduledFuture);
        logger.info("---添加定時(shí)任務(wù)--->" + id);
    }

    /**
     * 通過(guò)上述 put 方法的參數(shù)id(定時(shí)任務(wù)id)標(biāo)識(shí),將定時(shí)任務(wù)移除出 map
     * @param id
     */
    public static void delete(String id) {
        ScheduledFuture<?> scheduledFuture = scheduledFutureMap.get(id);
        // 條件判斷
        if (scheduledFuture != null && scheduledFuture.isCancelled()) {
            scheduledFuture.cancel(true);
        }
        scheduledFutureMap.remove(id);
        logger.info("---取消定時(shí)任務(wù)--->" + id);
    }
}

第二部分是關(guān)于結(jié)合實(shí)際業(yè)務(wù),引入實(shí)際業(yè)務(wù)數(shù)據(jù)的代碼demo。

/**
 * @author Created by zhuzqc on 2023/1/30 15:58
 */
@Slf4j
@Component
@EnableScheduling
public class TaskScheduleDemo{

    @Resource
    private AAAMapper aaaMapper;
    @Resource
    private BBBService bbbService;

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    // 引入外部的業(yè)務(wù)數(shù)據(jù)對(duì)象
    public void putHiredTask(CCCEntity cccEntity){
        //TODO: 將業(yè)務(wù)線(xiàn)程和定時(shí)觸發(fā)器交由線(xiàn)程池工具管理:創(chuàng)建業(yè)務(wù)線(xiàn)程對(duì)象,并對(duì)屬性賦初始化值(有參構(gòu)造)
        TaskSchedulerUtil.put(new TaskThreadDemo(cccEntity,aaaMapper,bbbService), new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                //獲取定時(shí)觸發(fā)器,這里可以獲取頁(yè)面的更新記錄,實(shí)現(xiàn)定時(shí)間隔的動(dòng)態(tài)調(diào)整
                Date nextExecTime = TaskTransUtils.StringToDateTime(cccEntity.getSendTime());
                //cron 表達(dá)式轉(zhuǎn)換工具類(lèi)
                String cron = TaskTransUtils.getDateCronTime(nextExecTime);
                try {
                    if (StringUtils.isBlank(cron)) {
                        // 提示參數(shù)為空
                        logger.info("trackScheduler定時(shí)器的 cron 參數(shù)為空!");
                        // 如果為空則賦默認(rèn)值,每天早上9:00
                        cron = "0 00 09 ? * *";
                    }
                    logger.info("-------定時(shí)任務(wù)執(zhí)行中:" + cron + "--------");
                    CronTrigger cronTrigger = new CronTrigger(cron);
                    nextExecTime = cronTrigger.nextExecutionTime(triggerContext);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.info(e.getMessage());
                }
                return nextExecTime;
            }
        },"該定時(shí)任務(wù)的id");
    }
}

五、文章小結(jié)

動(dòng)態(tài)定時(shí)任務(wù)的總結(jié)如下:

  • 單一使用ScheduledTaskRegistrar類(lèi),無(wú)法達(dá)到預(yù)期動(dòng)態(tài)調(diào)整定時(shí)任務(wù)的效果;
  • 實(shí)際的開(kāi)發(fā)場(chǎng)景中,需要業(yè)務(wù)邏輯代碼和觸發(fā)器代碼都能訪問(wèn)到外部業(yè)務(wù)數(shù)據(jù);
  • 配合ThreadPoolTaskScheduler類(lèi)和該類(lèi)中的schedule()方法可以達(dá)到動(dòng)態(tài)調(diào)整定時(shí)任務(wù)的效果。

以上就是SpringBoot ScheduledTaskRegistrar解決動(dòng)態(tài)定時(shí)任務(wù)思路詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot ScheduledTaskRegistrar的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 淺談一下Servlet的定義以及運(yùn)行原理

    淺談一下Servlet的定義以及運(yùn)行原理

    相信有很多剛?cè)胄械呐笥褧?huì)疑惑Servlet到底是個(gè)什么意思,那么這篇文章就來(lái)淺談一下到底什么是Servlet,以及Servlet的原理與如何寫(xiě)一個(gè)Servlet,,需要的朋友可以參考下
    2023-03-03
  • Springboot解決跨域問(wèn)題方案總結(jié)(包括Nginx,Gateway網(wǎng)關(guān)等)

    Springboot解決跨域問(wèn)題方案總結(jié)(包括Nginx,Gateway網(wǎng)關(guān)等)

    跨域問(wèn)題是瀏覽器為了保護(hù)用戶(hù)的信息安全,實(shí)施了同源策略(Same-Origin?Policy),即只允許頁(yè)面請(qǐng)求同源(相同協(xié)議、域名和端口)的資源,本文給大家總結(jié)了Springboot解決跨域問(wèn)題方案包括Nginx,Gateway網(wǎng)關(guān)等),需要的朋友可以參考下
    2024-03-03
  • 使用 Java 類(lèi) 實(shí)現(xiàn)Http協(xié)議

    使用 Java 類(lèi) 實(shí)現(xiàn)Http協(xié)議

    這篇文章主要介紹了用幾個(gè)Java類(lèi)簡(jiǎn)單的實(shí)現(xiàn)了Http協(xié)議相關(guān)資料,感興趣的的朋友可以參考下面具體的文章內(nèi)容
    2021-09-09
  • springboot實(shí)用配置詳細(xì)圖文教程

    springboot實(shí)用配置詳細(xì)圖文教程

    SpringBoot從本質(zhì)上來(lái)說(shuō)就是Spring,它通過(guò)了一些自己的特性幫助我們簡(jiǎn)化了Spring應(yīng)用程序的開(kāi)發(fā),下面這篇文章主要給大家介紹了關(guān)于springboot實(shí)用配置的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • JAVA 多態(tài)操作----父類(lèi)與子類(lèi)轉(zhuǎn)換問(wèn)題實(shí)例分析

    JAVA 多態(tài)操作----父類(lèi)與子類(lèi)轉(zhuǎn)換問(wèn)題實(shí)例分析

    這篇文章主要介紹了JAVA 多態(tài)操作----父類(lèi)與子類(lèi)轉(zhuǎn)換問(wèn)題,結(jié)合實(shí)例形式分析了JAVA 多態(tài)操作中父類(lèi)與子類(lèi)轉(zhuǎn)換問(wèn)題相關(guān)原理、操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2020-05-05
  • java內(nèi)存模型jvm虛擬機(jī)簡(jiǎn)要分析

    java內(nèi)存模型jvm虛擬機(jī)簡(jiǎn)要分析

    Java 內(nèi)存模型的主要目的是定義程序中各種變量的訪問(wèn)規(guī)則, 關(guān)注在虛擬機(jī)中把變量值存儲(chǔ)到內(nèi)存和從內(nèi)存中取出變量值這樣的底層細(xì)節(jié)
    2021-09-09
  • mybatis in查詢(xún)條件過(guò)長(zhǎng)的解決方案

    mybatis in查詢(xún)條件過(guò)長(zhǎng)的解決方案

    這篇文章主要介紹了mybatis in查詢(xún)條件過(guò)長(zhǎng)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 在Mac OS上安裝Java以及配置環(huán)境變量的基本方法

    在Mac OS上安裝Java以及配置環(huán)境變量的基本方法

    這篇文章主要介紹了在Mac OS上安裝Java以及配置環(huán)境變量的基本方法,包括查看所安裝Java版本的方法,需要的朋友可以參考下
    2015-10-10
  • 詳解Spring Data JPA動(dòng)態(tài)條件查詢(xún)的寫(xiě)法

    詳解Spring Data JPA動(dòng)態(tài)條件查詢(xún)的寫(xiě)法

    本篇文章主要介紹了Spring Data JPA動(dòng)態(tài)條件查詢(xún)的寫(xiě)法 ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-06-06
  • Java中Object類(lèi)的理解和使用

    Java中Object類(lèi)的理解和使用

    Object類(lèi)是java.lang包下的核心類(lèi),Object類(lèi)是所有類(lèi)的父類(lèi),何一個(gè)類(lèi)時(shí)候如果沒(méi)有明確的繼承一個(gè)父類(lèi)的話(huà),那么它就是Object的子類(lèi),本文將通過(guò)代碼示例詳細(xì)介紹一下Java中Object類(lèi)的理解和使用,需要的朋友可以參考下
    2023-06-06

最新評(píng)論