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

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

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

前言

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

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

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

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

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

一、問題背景

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

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

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

二、痛點(diǎn)所在

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

  • 只能按照指定的時(shí)間去執(zhí)行,更改執(zhí)行時(shí)間需要重啟服務(wù);
  • 無法刪除該定時(shí)任務(wù),或者刪除后無法再啟動(dòng)新的定時(shí)任務(wù)。
  • 業(yè)務(wù)邏輯與觸發(fā)器的代碼耦合度太高,無法將業(yè)務(wù)代碼從ScheduledTaskRegistrar類中抽離出去。
/**
 * @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線程注冊任務(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ù),無法對 cron 表達(dá)式進(jìn)行更改,如果更改則需要重新啟動(dòng)服務(wù),非常地“傻瓜”。

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

三、解決思路

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

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

該類中schedule(Runnable task, Trigger trigger)方法,通過分別傳入線程任務(wù)(業(yè)務(wù)邏輯)Trigger觸發(fā)器對象作為參數(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);
		}
	}

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

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

四、代碼示例

代碼示例分為兩部分:

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

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

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

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

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

    static {
        threadPoolTaskScheduler.initialize();
    }

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

    /**
     * 通過上述 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ù)對象
    public void putHiredTask(CCCEntity cccEntity){
        //TODO: 將業(yè)務(wù)線程和定時(shí)觸發(fā)器交由線程池工具管理:創(chuàng)建業(yè)務(wù)線程對象,并對屬性賦初始化值(有參構(gòu)造)
        TaskSchedulerUtil.put(new TaskThreadDemo(cccEntity,aaaMapper,bbbService), new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                //獲取定時(shí)觸發(fā)器,這里可以獲取頁面的更新記錄,實(shí)現(xiàn)定時(shí)間隔的動(dòng)態(tài)調(diào)整
                Date nextExecTime = TaskTransUtils.StringToDateTime(cccEntity.getSendTime());
                //cron 表達(dá)式轉(zhuǎn)換工具類
                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類,無法達(dá)到預(yù)期動(dòng)態(tài)調(diào)整定時(shí)任務(wù)的效果;
  • 實(shí)際的開發(fā)場景中,需要業(yè)務(wù)邏輯代碼和觸發(fā)器代碼都能訪問到外部業(yè)務(wù)數(shù)據(jù);
  • 配合ThreadPoolTaskScheduler類和該類中的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的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

昌黎县| 河东区| 尚义县| 甘南县| 金昌市| 宁晋县| 邓州市| 固安县| 海南省| 抚州市| 麻阳| 观塘区| 屯昌县| 平阴县| 富源县| 贺州市| 运城市| 福建省| 陆良县| 清徐县| 尖扎县| 密山市| 南陵县| 永定县| 晋江市| 黄冈市| 沂源县| 白朗县| 长岭县| 开化县| 湛江市| 通榆县| 葵青区| 大邑县| 湘阴县| 奉化市| 改则县| 宜城市| 抚宁县| 青田县| 新兴县|