0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

SpringBoot如何實(shí)現(xiàn)動(dòng)態(tài)增刪啟停定時(shí)任務(wù)

Android編程精選 ? 來(lái)源:簡(jiǎn)書(shū) ? 作者:jessehua ? 2021-09-24 09:49 ? 次閱讀
在spring boot項(xiàng)目中,可以通過(guò)@EnableScheduling注解和@Scheduled注解實(shí)現(xiàn)定時(shí)任務(wù),也可以通過(guò)SchedulingConfigurer接口來(lái)實(shí)現(xiàn)定時(shí)任務(wù)。但是這兩種方式不能動(dòng)態(tài)添加、刪除、啟動(dòng)、停止任務(wù)。

要實(shí)現(xiàn)動(dòng)態(tài)增刪啟停定時(shí)任務(wù)功能,比較廣泛的做法是集成Quartz框架。但是本人的開(kāi)發(fā)原則是:在滿足項(xiàng)目需求的情況下,盡量少的依賴其它框架,避免項(xiàng)目過(guò)于臃腫和復(fù)雜。

查看spring-context這個(gè)jar包中org.springframework.scheduling.ScheduledTaskRegistrar這個(gè)類(lèi)的源代碼,發(fā)現(xiàn)可以通過(guò)改造這個(gè)類(lèi)就能實(shí)現(xiàn)動(dòng)態(tài)增刪啟停定時(shí)任務(wù)功能。

e8f20370-1085-11ec-8fb8-12bb97331649.jpg定時(shí)任務(wù)列表頁(yè)e90122f6-1085-11ec-8fb8-12bb97331649.jpg定時(shí)任務(wù)執(zhí)行日志

添加執(zhí)行定時(shí)任務(wù)的線程池配置類(lèi)

@Configuration
publicclassSchedulingConfig{
@Bean
publicTaskSchedulertaskScheduler(){
ThreadPoolTaskSchedulertaskScheduler=newThreadPoolTaskScheduler();
//定時(shí)任務(wù)執(zhí)行線程池核心線程數(shù)
taskScheduler.setPoolSize(4);
taskScheduler.setRemoveOnCancelPolicy(true);
taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");
returntaskScheduler;
}
}

添加ScheduledFuture的包裝類(lèi)。ScheduledFuture是ScheduledExecutorService定時(shí)任務(wù)線程池的執(zhí)行結(jié)果。

publicfinalclassScheduledTask{

volatileScheduledFuturefuture;

/**
*取消定時(shí)任務(wù)
*/
publicvoidcancel(){
ScheduledFuturefuture=this.future;
if(future!=null){
future.cancel(true);
}
}
}

添加Runnable接口實(shí)現(xiàn)類(lèi),被定時(shí)任務(wù)線程池調(diào)用,用來(lái)執(zhí)行指定bean里面的方法。

publicclassSchedulingRunnableimplementsRunnable{

privatestaticfinalLoggerlogger=LoggerFactory.getLogger(SchedulingRunnable.class);

privateStringbeanName;

privateStringmethodName;

privateStringparams;

publicSchedulingRunnable(StringbeanName,StringmethodName){
this(beanName,methodName,null);
}

publicSchedulingRunnable(StringbeanName,StringmethodName,Stringparams){
this.beanName=beanName;
this.methodName=methodName;
this.params=params;
}

@Override
publicvoidrun(){
logger.info("定時(shí)任務(wù)開(kāi)始執(zhí)行- bean:{},方法:{},參數(shù):{}",beanName,methodName,params);
longstartTime=System.currentTimeMillis();

try{
Objecttarget=SpringContextUtils.getBean(beanName);

Methodmethod=null;
if(StringUtils.isNotEmpty(params)){
method=target.getClass().getDeclaredMethod(methodName,String.class);
}else{
method=target.getClass().getDeclaredMethod(methodName);
}

ReflectionUtils.makeAccessible(method);
if(StringUtils.isNotEmpty(params)){
method.invoke(target,params);
}else{
method.invoke(target);
}
}catch(Exceptionex){
logger.error(String.format("定時(shí)任務(wù)執(zhí)行異常- bean:%s,方法:%s,參數(shù):%s ",beanName,methodName,params),ex);
}

longtimes=System.currentTimeMillis()-startTime;
logger.info("定時(shí)任務(wù)執(zhí)行結(jié)束- bean:{},方法:{},參數(shù):{},耗時(shí):{}毫秒",beanName,methodName,params,times);
}

@Override
publicbooleanequals(Objecto){
if(this==o)returntrue;
if(o==null||getClass()!=o.getClass())returnfalse;
SchedulingRunnablethat=(SchedulingRunnable)o;
if(params==null){
returnbeanName.equals(that.beanName)&&
methodName.equals(that.methodName)&&
that.params==null;
}

returnbeanName.equals(that.beanName)&&
methodName.equals(that.methodName)&&
params.equals(that.params);
}

@Override
publicinthashCode(){
if(params==null){
returnObjects.hash(beanName,methodName);
}

returnObjects.hash(beanName,methodName,params);
}
}

添加定時(shí)任務(wù)注冊(cè)類(lèi),用來(lái)增加、刪除定時(shí)任務(wù)。

@Component
publicclassCronTaskRegistrarimplementsDisposableBean{

privatefinalMapscheduledTasks=newConcurrentHashMap<>(16);

@Autowired
privateTaskSchedulertaskScheduler;

publicTaskSchedulergetScheduler(){
returnthis.taskScheduler;
}

publicvoidaddCronTask(Runnabletask,StringcronExpression){
addCronTask(newCronTask(task,cronExpression));
}

publicvoidaddCronTask(CronTaskcronTask){
if(cronTask!=null){
Runnabletask=cronTask.getRunnable();
if(this.scheduledTasks.containsKey(task)){
removeCronTask(task);
}

this.scheduledTasks.put(task,scheduleCronTask(cronTask));
}
}

publicvoidremoveCronTask(Runnabletask){
ScheduledTaskscheduledTask=this.scheduledTasks.remove(task);
if(scheduledTask!=null)
scheduledTask.cancel();
}

publicScheduledTaskscheduleCronTask(CronTaskcronTask){
ScheduledTaskscheduledTask=newScheduledTask();
scheduledTask.future=this.taskScheduler.schedule(cronTask.getRunnable(),cronTask.getTrigger());

returnscheduledTask;
}


@Override
publicvoiddestroy(){
for(ScheduledTasktask:this.scheduledTasks.values()){
task.cancel();
}

this.scheduledTasks.clear();
}
}

添加定時(shí)任務(wù)示例類(lèi)

@Component("demoTask")
publicclassDemoTask{
publicvoidtaskWithParams(Stringparams){
System.out.println("執(zhí)行有參示例任務(wù):"+params);
}

publicvoidtaskNoParams(){
System.out.println("執(zhí)行無(wú)參示例任務(wù)");
}
}

定時(shí)任務(wù)數(shù)據(jù)庫(kù)表設(shè)計(jì)

添加定時(shí)任務(wù)實(shí)體類(lèi)

publicclassSysJobPO{
/**
*任務(wù)ID
*/
privateIntegerjobId;
/**
*bean名稱(chēng)
*/
privateStringbeanName;
/**
*方法名稱(chēng)
*/
privateStringmethodName;
/**
*方法參數(shù)
*/
privateStringmethodParams;
/**
*cron表達(dá)式
*/
privateStringcronExpression;
/**
*狀態(tài)(1正常0暫停)
*/
privateIntegerjobStatus;
/**
*備注
*/
privateStringremark;
/**
*創(chuàng)建時(shí)間
*/
privateDatecreateTime;
/**
*更新時(shí)間
*/
privateDateupdateTime;

publicIntegergetJobId(){
returnjobId;
}

publicvoidsetJobId(IntegerjobId){
this.jobId=jobId;
}

publicStringgetBeanName(){
returnbeanName;
}

publicvoidsetBeanName(StringbeanName){
this.beanName=beanName;
}

publicStringgetMethodName(){
returnmethodName;
}

publicvoidsetMethodName(StringmethodName){
this.methodName=methodName;
}

publicStringgetMethodParams(){
returnmethodParams;
}

publicvoidsetMethodParams(StringmethodParams){
this.methodParams=methodParams;
}

publicStringgetCronExpression(){
returncronExpression;
}

publicvoidsetCronExpression(StringcronExpression){
this.cronExpression=cronExpression;
}

publicIntegergetJobStatus(){
returnjobStatus;
}

publicvoidsetJobStatus(IntegerjobStatus){
this.jobStatus=jobStatus;
}

publicStringgetRemark(){
returnremark;
}

publicvoidsetRemark(Stringremark){
this.remark=remark;
}

publicDategetCreateTime(){
returncreateTime;
}

publicvoidsetCreateTime(DatecreateTime){
this.createTime=createTime;
}

publicDategetUpdateTime(){
returnupdateTime;
}

publicvoidsetUpdateTime(DateupdateTime){
this.updateTime=updateTime;
}

}
booleansuccess=sysJobRepository.addSysJob(sysJob);
if(!success)
returnOperationResUtils.fail("新增失敗");
else{
if(sysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(sysJob.getBeanName(),sysJob.getMethodName(),sysJob.getMethodParams());
cronTaskRegistrar.addCronTask(task,sysJob.getCronExpression());
}
}

returnOperationResUtils.success();

修改定時(shí)任務(wù),先移除原來(lái)的任務(wù),再啟動(dòng)新任務(wù)

booleansuccess=sysJobRepository.editSysJob(sysJob);
if(!success)
returnOperationResUtils.fail("編輯失敗");
else{
//先移除再添加
if(existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(existedSysJob.getBeanName(),existedSysJob.getMethodName(),existedSysJob.getMethodParams());
cronTaskRegistrar.removeCronTask(task);
}

if(sysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(sysJob.getBeanName(),sysJob.getMethodName(),sysJob.getMethodParams());
cronTaskRegistrar.addCronTask(task,sysJob.getCronExpression());
}
}

returnOperationResUtils.success();

刪除定時(shí)任務(wù)

booleansuccess=sysJobRepository.deleteSysJobById(req.getJobId());
if(!success)
returnOperationResUtils.fail("刪除失敗");
else{
if(existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(existedSysJob.getBeanName(),existedSysJob.getMethodName(),existedSysJob.getMethodParams());
cronTaskRegistrar.removeCronTask(task);
}
}

returnOperationResUtils.success();

定時(shí)任務(wù)啟動(dòng)/停止?fàn)顟B(tài)切換

if(existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())){
SchedulingRunnabletask=newSchedulingRunnable(existedSysJob.getBeanName(),existedSysJob.getMethodName(),existedSysJob.getMethodParams());
cronTaskRegistrar.addCronTask(task,existedSysJob.getCronExpression());
}else{
SchedulingRunnabletask=newSchedulingRunnable(existedSysJob.getBeanName(),existedSysJob.getMethodName(),existedSysJob.getMethodParams());
cronTaskRegistrar.removeCronTask(task);
}

添加實(shí)現(xiàn)了CommandLineRunner接口的SysJobRunner類(lèi),當(dāng)spring boot項(xiàng)目啟動(dòng)完成后,加載數(shù)據(jù)庫(kù)里狀態(tài)為正常的定時(shí)任務(wù)。

@Service
publicclassSysJobRunnerimplementsCommandLineRunner{

privatestaticfinalLoggerlogger=LoggerFactory.getLogger(SysJobRunner.class);

@Autowired
privateISysJobRepositorysysJobRepository;

@Autowired
privateCronTaskRegistrarcronTaskRegistrar;

@Override
publicvoidrun(String...args){
//初始加載數(shù)據(jù)庫(kù)里狀態(tài)為正常的定時(shí)任務(wù)
ListjobList=sysJobRepository.getSysJobListByStatus(SysJobStatus.NORMAL.ordinal());
if(CollectionUtils.isNotEmpty(jobList)){
for(SysJobPOjob:jobList){
SchedulingRunnabletask=newSchedulingRunnable(job.getBeanName(),job.getMethodName(),job.getMethodParams());
cronTaskRegistrar.addCronTask(task,job.getCronExpression());
}

logger.info("定時(shí)任務(wù)已加載完畢...");
}
}
}

工具類(lèi)SpringContextUtils,用來(lái)從spring容器里獲取bean

@Component
publicclassSpringContextUtilsimplementsApplicationContextAware{

privatestaticApplicationContextapplicationContext;

@Override
publicvoidsetApplicationContext(ApplicationContextapplicationContext)
throwsBeansException{
SpringContextUtils.applicationContext=applicationContext;
}

publicstaticObjectgetBean(Stringname){
returnapplicationContext.getBean(name);
}

publicstaticTgetBean(ClassrequiredType){
returnapplicationContext.getBean(requiredType);
}

publicstaticTgetBean(Stringname,ClassrequiredType){
returnapplicationContext.getBean(name,requiredType);
}

publicstaticbooleancontainsBean(Stringname){
returnapplicationContext.containsBean(name);
}

publicstaticbooleanisSingleton(Stringname){
returnapplicationContext.isSingleton(name);
}

publicstaticClassgetType(Stringname){
returnapplicationContext.getType(name);
}
}

本文完,參考本文代碼可成功運(yùn)行,親測(cè)!

(感謝閱讀,希望對(duì)你所有幫助)來(lái)源:www.jianshu.com/p/0f68936393fd
編輯:jq
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 源代碼
    +關(guān)注

    關(guān)注

    96

    文章

    2945

    瀏覽量

    66748
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    340

    瀏覽量

    14344
  • Boot
    +關(guān)注

    關(guān)注

    0

    文章

    149

    瀏覽量

    35839
  • SpringBoot
    +關(guān)注

    關(guān)注

    0

    文章

    173

    瀏覽量

    179

原文標(biāo)題:告別硬編碼,SpringBoot實(shí)現(xiàn)動(dòng)態(tài)增刪啟停定時(shí)任務(wù)

文章出處:【微信號(hào):AndroidPush,微信公眾號(hào):Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Linux計(jì)劃任務(wù)介紹

    1.計(jì)劃任務(wù)定時(shí)任務(wù))基本概述 1.什么是crond crond就是計(jì)劃任務(wù),類(lèi)似于我們平時(shí)生活中的鬧鐘。定點(diǎn)執(zhí)行。 2.為什么要使用crond?crond主要是做一些周期性的任務(wù),
    的頭像 發(fā)表于 11-24 15:49 ?287次閱讀

    定時(shí)器技術(shù):Air780E如何革新定時(shí)任務(wù)管理?

    今天講的是關(guān)于Air780E如何革新定時(shí)任務(wù)管理的內(nèi)容,希望大家有所收獲。
    的頭像 發(fā)表于 11-07 13:50 ?236次閱讀
    <b class='flag-5'>定時(shí)</b>器技術(shù):Air780E如何革新<b class='flag-5'>定時(shí)任務(wù)</b>管理?

    mysql定時(shí)備份任務(wù)

    在生產(chǎn)環(huán)境上,為了避免數(shù)據(jù)的丟失,通常情況下都會(huì)定時(shí)的對(duì)數(shù)據(jù)庫(kù)進(jìn)行備份。而Linux的crontab指令則可以幫助我們實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)定時(shí)進(jìn)行備份。首先我們來(lái)簡(jiǎn)單了解crontab指令,如果你會(huì)了請(qǐng)?zhí)较乱粋€(gè)內(nèi)容mysql備份。
    的頭像 發(fā)表于 10-31 10:07 ?164次閱讀

    變頻器外接按鈕如何接線

    以下是變頻器外接按鈕接線的一般步驟: 確定變頻器的型號(hào)和規(guī)格,查閱其技術(shù)手冊(cè)以獲取接線圖和接線說(shuō)明。 準(zhǔn)備所需的工具和材料,包括螺絲刀、剝線鉗、接線端子、接線盒等。 關(guān)閉電源并斷開(kāi)變頻器與電源
    的頭像 發(fā)表于 08-25 10:49 ?1696次閱讀

    linux定時(shí)任務(wù)的用法總結(jié)

    習(xí)慣了使用 windows 的計(jì)劃任務(wù),使用 linux 中的 crontab 管理定時(shí)任務(wù)時(shí)很不適應(yīng)。
    的頭像 發(fā)表于 08-14 18:16 ?851次閱讀
    linux<b class='flag-5'>定時(shí)任務(wù)</b>的用法總結(jié)

    ESP8266如何實(shí)現(xiàn)時(shí)間小于3us的定時(shí)任務(wù)?

    實(shí)現(xiàn)一個(gè)穩(wěn)定的軟串口,現(xiàn)有的軟串口程序是通過(guò)中斷實(shí)現(xiàn)的,但中斷好像會(huì)被其他中斷打斷,導(dǎo)致數(shù)據(jù)丟失,定時(shí)器按文檔上的說(shuō)法,只能大于50us,能不能實(shí)現(xiàn)時(shí)間小于3us的
    發(fā)表于 07-19 06:13

    定時(shí)器的工作方式介紹

    實(shí)現(xiàn)周期性事件的硬件模塊。它可以用于實(shí)現(xiàn)各種定時(shí)任務(wù),如定時(shí)中斷、PWM(脈沖寬度調(diào)制)輸出、頻率測(cè)量等。定時(shí)器通常由一個(gè)計(jì)數(shù)器、一個(gè)時(shí)鐘
    的頭像 發(fā)表于 07-12 10:29 ?955次閱讀

    長(zhǎng)持續(xù)時(shí)間定時(shí)器電路圖 時(shí)間定時(shí)器的工作原理和功能

    的處理,都離不開(kāi)定時(shí)器的精確控制。時(shí)間定時(shí)器通常由硬件和軟件兩部分組成,硬件部分通過(guò)計(jì)時(shí)器芯片或計(jì)數(shù)器來(lái)實(shí)現(xiàn)時(shí)間的度量和計(jì)算,而軟件部分則是通過(guò)編程語(yǔ)言提供的函數(shù)或類(lèi)庫(kù)來(lái)設(shè)置和處理定時(shí)任務(wù)
    的頭像 發(fā)表于 06-24 17:34 ?1917次閱讀
    長(zhǎng)持續(xù)時(shí)間<b class='flag-5'>定時(shí)</b>器電路圖 時(shí)間<b class='flag-5'>定時(shí)</b>器的工作原理和功能

    PLC工程示例之步進(jìn)電機(jī)

    電子發(fā)燒友網(wǎng)站提供《PLC工程示例之步進(jìn)電機(jī).rar》資料免費(fèi)下載
    發(fā)表于 06-11 09:09 ?8次下載

    在物通博聯(lián)工業(yè)智能網(wǎng)關(guān)的本地配置界面(WEB)直接配置定時(shí)控制任務(wù)

    開(kāi)關(guān),可實(shí)現(xiàn)全年定時(shí)任務(wù)自動(dòng)執(zhí)行。在多個(gè)任務(wù)日期重疊時(shí),可選執(zhí)行高等級(jí)的還是并行執(zhí)行。設(shè)有一個(gè)遠(yuǎn)程和本地的控制點(diǎn),默認(rèn)為本地狀態(tài),網(wǎng)關(guān)自己執(zhí)行設(shè)置的任務(wù),當(dāng)用戶將改控制點(diǎn)切換為遠(yuǎn)程時(shí)可
    的頭像 發(fā)表于 04-24 17:21 ?537次閱讀
    在物通博聯(lián)工業(yè)智能網(wǎng)關(guān)的本地配置界面(WEB)直接配置<b class='flag-5'>定時(shí)</b>控制<b class='flag-5'>任務(wù)</b>

    基于VC的三相異步電機(jī)系統(tǒng)的設(shè)計(jì)

    針對(duì)小功率三相異步電機(jī)的,當(dāng)前國(guó)內(nèi)外還較多地采用繼電器、接觸器等控制電器來(lái)實(shí)現(xiàn)自動(dòng)控制。由于繼電接觸器控制系統(tǒng)是通過(guò)觸點(diǎn)的機(jī)械運(yùn)動(dòng)來(lái)通斷主、控回路,然而觸點(diǎn)因?yàn)殡?、機(jī)械和化學(xué)的原因易于磨損,并且在高沖擊、振蕩的工作環(huán)境下,觸
    發(fā)表于 02-15 17:21 ?517次閱讀
    基于VC的三相異步電機(jī)<b class='flag-5'>啟</b><b class='flag-5'>停</b>系統(tǒng)的設(shè)計(jì)

    使用TC21x的GPT實(shí)現(xiàn)1m計(jì)時(shí)器執(zhí)行定時(shí)任務(wù),怎么配置GTM和GPT?

    專(zhuān)家們好,我想使用TC21x的GPT實(shí)現(xiàn)1m計(jì)時(shí)器執(zhí)行定時(shí)任務(wù),不知道怎么配置GTM和GPT?
    發(fā)表于 02-06 06:47

    鴻蒙ArkUI開(kāi)發(fā)-實(shí)現(xiàn)增刪Tab頁(yè)簽

    本文以瀏覽器中增加或刪除頁(yè)簽為例,實(shí)現(xiàn)Tabs中頁(yè)簽的增刪功能。
    的頭像 發(fā)表于 01-29 18:43 ?1590次閱讀
    鴻蒙ArkUI開(kāi)發(fā)-<b class='flag-5'>實(shí)現(xiàn)</b><b class='flag-5'>增刪</b>Tab頁(yè)簽

    鴻蒙原生應(yīng)用/元服務(wù)開(kāi)發(fā)-長(zhǎng)時(shí)任務(wù)

    概述 功能介紹 應(yīng)用退至后臺(tái)后,對(duì)于在后臺(tái)需要長(zhǎng)時(shí)間運(yùn)行用戶可感知的任務(wù),例如播放音樂(lè)、導(dǎo)航等。為防止應(yīng)用進(jìn)程被掛起,導(dǎo)致對(duì)應(yīng)功能異常,可以申請(qǐng)長(zhǎng)時(shí)任務(wù),使應(yīng)用在后臺(tái)長(zhǎng)時(shí)間運(yùn)行。申請(qǐng)長(zhǎng)時(shí)任務(wù)后,系統(tǒng)
    發(fā)表于 01-09 10:52

    任務(wù)調(diào)度系統(tǒng)設(shè)計(jì)的核心邏輯

    Redis的讀寫(xiě)性能極好,分布式鎖也比Quartz數(shù)據(jù)庫(kù)行級(jí)鎖更輕量級(jí)。當(dāng)然Redis鎖也可以替換成Zookeeper鎖,也是同樣的機(jī)制。 在小型項(xiàng)目中,使用:定時(shí)任務(wù)框架(Quartz/Spring Schedule)和 分布式鎖(redis/zookeeper)有不錯(cuò)的效果。
    的頭像 發(fā)表于 01-02 15:09 ?888次閱讀
    <b class='flag-5'>任務(wù)</b>調(diào)度系統(tǒng)設(shè)計(jì)的核心邏輯