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

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

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

求一種SpringBoot定時(shí)任務(wù)動(dòng)態(tài)管理通用解決方案

Android編程精選 ? 來源:CSDN ? 2023-02-03 09:49 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

一、功能說明

SpringBoot的定時(shí)任務(wù)的加強(qiáng)工具,實(shí)現(xiàn)對SpringBoot原生的定時(shí)任務(wù)進(jìn)行動(dòng)態(tài)管理,完全兼容原生@Scheduled注解,無需對原本的定時(shí)任務(wù)進(jìn)行修改

二、快速使用

具體的功能已經(jīng)封裝成SpringBoot-starter即插即用


com.github.guoyixing
spring-boot-starter-super-scheduled
0.3.1

三、實(shí)現(xiàn)原理

1、動(dòng)態(tài)管理實(shí)現(xiàn)

(1) 配置管理介紹

@Component("superScheduledConfig")
publicclassSuperScheduledConfig{
/**
*執(zhí)行定時(shí)任務(wù)的線程池
*/
privateThreadPoolTaskSchedulertaskScheduler;

/**
*定時(shí)任務(wù)名稱與定時(shí)任務(wù)回調(diào)鉤子的關(guān)聯(lián)關(guān)系容器
*/
privateMapnameToScheduledFuture=newConcurrentHashMap<>();

/**
*定時(shí)任務(wù)名稱與定時(shí)任務(wù)需要執(zhí)行的邏輯的關(guān)聯(lián)關(guān)系容器
*/
privateMapnameToRunnable=newConcurrentHashMap<>();

/**
*定時(shí)任務(wù)名稱與定時(shí)任務(wù)的源信息的關(guān)聯(lián)關(guān)系容器
*/
privateMapnameToScheduledSource=newConcurrentHashMap<>();
/*普通的get/sets省略*/
}

(2) 使用后處理器攔截SpringBoot原本的定時(shí)任務(wù)

實(shí)現(xiàn)ApplicationContextAware接口拿到SpringBoot的上下文

實(shí)現(xiàn)BeanPostProcessor接口,將這個(gè)類標(biāo)記為后處理器,后處理器會(huì)在每個(gè)bean實(shí)例化之后執(zhí)行

使用@DependsOn注解強(qiáng)制依賴SuperScheduledConfig類,讓SpringBoot實(shí)例化SuperScheduledPostProcessor類之前先實(shí)例化SuperScheduledConfig類

主要實(shí)現(xiàn)邏輯在postProcessAfterInitialization()方法中

ff50efb0-a33b-11ed-bfe3-dac502259ad0.png

@DependsOn({"superScheduledConfig"})
@Component
@Order
publicclassSuperScheduledPostProcessorimplementsBeanPostProcessor,ApplicationContextAware{
protectedfinalLoglogger=LogFactory.getLog(getClass());

privateApplicationContextapplicationContext;

/**
*實(shí)例化bean之前的操作
*@parambeanbean實(shí)例
*@parambeanNamebean的Name
*/
@Override
publicObjectpostProcessBeforeInitialization(Objectbean,StringbeanName)throwsBeansException{
returnbean;
}

/**
*實(shí)例化bean之后的操作
*@parambeanbean實(shí)例
*@parambeanNamebean的Name
*/
@Override
publicObjectpostProcessAfterInitialization(Objectbean,
StringbeanName)throwsBeansException{
//1.獲取配置管理器
SuperScheduledConfigsuperScheduledConfig=applicationContext.getBean(SuperScheduledConfig.class);

//2.獲取當(dāng)前實(shí)例化完成的bean的所有方法
Method[]methods=bean.getClass().getDeclaredMethods();
//循環(huán)處理對每個(gè)方法逐一處理
if(methods.length>0){
for(Methodmethod:methods){
//3.嘗試在該方法上獲取@Scheduled注解(SpringBoot的定時(shí)任務(wù)注解)
Scheduledannotation=method.getAnnotation(Scheduled.class);
//如果無法獲取到@Scheduled注解,就跳過這個(gè)方法
if(annotation==null){
continue;
}
//4.創(chuàng)建定時(shí)任務(wù)的源屬性
//創(chuàng)建定時(shí)任務(wù)的源屬性(用來記錄定時(shí)任務(wù)的配置,初始化的時(shí)候記錄的是注解上原本的屬性)
ScheduledSourcescheduledSource=newScheduledSource(annotation,method,bean);
//對注解上獲取到源屬性中的屬性進(jìn)行檢測
if(!scheduledSource.check()){
thrownewSuperScheduledException("在"+beanName+"Bean中"+method.getName()+"方法的注解參數(shù)錯(cuò)誤");
}
//生成定時(shí)任務(wù)的名稱(id),使用beanName+“.”+方法名
Stringname=beanName+"."+method.getName();
//將以key-value的形式,將源數(shù)據(jù)存入配置管理器中,key:定時(shí)任務(wù)的名稱value:源數(shù)據(jù)
superScheduledConfig.addScheduledSource(name,scheduledSource);
try{
//5.將原本SpringBoot的定時(shí)任務(wù)取消掉
clearOriginalScheduled(annotation);
}catch(Exceptione){
thrownewSuperScheduledException("在關(guān)閉原始方法"+beanName+method.getName()+"時(shí)出現(xiàn)錯(cuò)誤");
}
}
}
//最后bean保持原有返回
returnbean;
}

/**
*修改注解原先的屬性
*@paramannotation注解實(shí)例對象
*@throwsException
*/
privatevoidclearOriginalScheduled(Scheduledannotation)throwsException{
changeAnnotationValue(annotation,"cron",Scheduled.CRON_DISABLED);
changeAnnotationValue(annotation,"fixedDelay",-1L);
changeAnnotationValue(annotation,"fixedDelayString","");
changeAnnotationValue(annotation,"fixedRate",-1L);
changeAnnotationValue(annotation,"fixedRateString","");
changeAnnotationValue(annotation,"initialDelay",-1L);
changeAnnotationValue(annotation,"initialDelayString","");
}


/**
*獲取SpringBoot的上下文
*@paramapplicationContextSpringBoot的上下文
*/
@Override
publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{
this.applicationContext=applicationContext;
}
}

(3) 使用ApplicationRunner初始化自定義的定時(shí)任務(wù)運(yùn)行器

實(shí)現(xiàn)ApplicationContextAware接口拿到SpringBoot的上下文

使用@DependsOn注解強(qiáng)制依賴threadPoolTaskScheduler類

實(shí)現(xiàn)ApplicationRunner接口,在所有bean初始化結(jié)束之后,運(yùn)行自定義邏輯

主要實(shí)現(xiàn)邏輯在run()方法中

ff6ca778-a33b-11ed-bfe3-dac502259ad0.png

@DependsOn("threadPoolTaskScheduler")
@Component
publicclassSuperScheduledApplicationRunnerimplementsApplicationRunner,ApplicationContextAware{
protectedfinalLoglogger=LogFactory.getLog(getClass());
privateDateTimeFormatterdf=DateTimeFormatter.ofPattern("yyyy-MM-ddHHss");
privateApplicationContextapplicationContext;

/**
*定時(shí)任務(wù)配置管理器
*/
@Autowired
privateSuperScheduledConfigsuperScheduledConfig;
/**
*定時(shí)任務(wù)執(zhí)行線程
*/
@Autowired
privateThreadPoolTaskSchedulerthreadPoolTaskScheduler;

@Override
publicvoidrun(ApplicationArgumentsargs){
//1.定時(shí)任務(wù)配置管理器中緩存定時(shí)任務(wù)執(zhí)行線程
superScheduledConfig.setTaskScheduler(threadPoolTaskScheduler);
//2.獲取所有定時(shí)任務(wù)源數(shù)據(jù)
MapnameToScheduledSource=superScheduledConfig.getNameToScheduledSource();
//逐一處理定時(shí)任務(wù)
for(Stringname:nameToScheduledSource.keySet()){
//3.獲取定時(shí)任務(wù)源數(shù)據(jù)
ScheduledSourcescheduledSource=nameToScheduledSource.get(name);
//4.獲取所有增強(qiáng)類
String[]baseStrengthenBeanNames=applicationContext.getBeanNamesForType(BaseStrengthen.class);
//5.創(chuàng)建執(zhí)行控制器
SuperScheduledRunnablerunnable=newSuperScheduledRunnable();
//配置執(zhí)行控制器
runnable.setMethod(scheduledSource.getMethod());
runnable.setBean(scheduledSource.getBean());
//6.逐一處理增強(qiáng)類(增強(qiáng)器實(shí)現(xiàn)原理后面具體分析)
Listpoints=newArrayList<>(baseStrengthenBeanNames.length);
for(StringbaseStrengthenBeanName:baseStrengthenBeanNames){
//7.將增強(qiáng)器代理成point
ObjectbaseStrengthenBean=applicationContext.getBean(baseStrengthenBeanName);
//創(chuàng)建代理
Pointproxy=ProxyUtils.getInstance(Point.class,newRunnableBaseInterceptor(baseStrengthenBean,runnable));
proxy.setSuperScheduledName(name);
//8.所有的points連成起來
points.add(proxy);
}
//將point形成調(diào)用鏈
runnable.setChain(newChain(points));
//將執(zhí)行邏輯封裝并緩存到定時(shí)任務(wù)配置管理器中
superScheduledConfig.addRunnable(name,runnable::invoke);
try{
//8.啟動(dòng)定時(shí)任務(wù)
ScheduledFutureschedule=ScheduledFutureFactory.create(threadPoolTaskScheduler
,scheduledSource,runnable::invoke);
//將線程回調(diào)鉤子存到任務(wù)配置管理器中
superScheduledConfig.addScheduledFuture(name,schedule);
logger.info(df.format(LocalDateTime.now())+"任務(wù)"+name+"已經(jīng)啟動(dòng)...");

}catch(Exceptione){
thrownewSuperScheduledException("任務(wù)"+name+"啟動(dòng)失敗,錯(cuò)誤信息:"+e.getLocalizedMessage());
}
}
}

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

(4) 進(jìn)行動(dòng)態(tài)管理

@Component
publicclassSuperScheduledManager{
protectedfinalLoglogger=LogFactory.getLog(getClass());
privateDateTimeFormatterdf=DateTimeFormatter.ofPattern("yyyy-MM-ddHHss");

@Autowired
privateSuperScheduledConfigsuperScheduledConfig;

/**
*修改Scheduled的執(zhí)行周期
*
*@paramnamescheduled的名稱
*@paramcroncron表達(dá)式
*/
publicvoidsetScheduledCron(Stringname,Stringcron){
//終止原先的任務(wù)
cancelScheduled(name);
//創(chuàng)建新的任務(wù)
ScheduledSourcescheduledSource=superScheduledConfig.getScheduledSource(name);
scheduledSource.clear();
scheduledSource.setCron(cron);
addScheduled(name,scheduledSource);
}

/**
*修改Scheduled的fixedDelay
*
*@paramnamescheduled的名稱
*@paramfixedDelay上一次執(zhí)行完畢時(shí)間點(diǎn)之后多長時(shí)間再執(zhí)行
*/
publicvoidsetScheduledFixedDelay(Stringname,LongfixedDelay){
//終止原先的任務(wù)
cancelScheduled(name);
//創(chuàng)建新的任務(wù)
ScheduledSourcescheduledSource=superScheduledConfig.getScheduledSource(name);
scheduledSource.clear();
scheduledSource.setFixedDelay(fixedDelay);
addScheduled(name,scheduledSource);
}

/**
*修改Scheduled的fixedRate
*
*@paramnamescheduled的名稱
*@paramfixedRate上一次開始執(zhí)行之后多長時(shí)間再執(zhí)行
*/
publicvoidsetScheduledFixedRate(Stringname,LongfixedRate){
//終止原先的任務(wù)
cancelScheduled(name);
//創(chuàng)建新的任務(wù)
ScheduledSourcescheduledSource=superScheduledConfig.getScheduledSource(name);
scheduledSource.clear();
scheduledSource.setFixedRate(fixedRate);
addScheduled(name,scheduledSource);
}

/**
*查詢所有啟動(dòng)的Scheduled
*/
publicListgetRunScheduledName(){
Setnames=superScheduledConfig.getNameToScheduledFuture().keySet();
returnnewArrayList<>(names);
}

/**
*查詢所有的Scheduled
*/
publicListgetAllSuperScheduledName(){
Setnames=superScheduledConfig.getNameToRunnable().keySet();
returnnewArrayList<>(names);
}

/**
*終止Scheduled
*
*@paramnamescheduled的名稱
*/
publicvoidcancelScheduled(Stringname){
ScheduledFuturescheduledFuture=superScheduledConfig.getScheduledFuture(name);
scheduledFuture.cancel(true);
superScheduledConfig.removeScheduledFuture(name);
logger.info(df.format(LocalDateTime.now())+"任務(wù)"+name+"已經(jīng)終止...");
}

/**
*啟動(dòng)Scheduled
*
*@paramnamescheduled的名稱
*@paramscheduledSource定時(shí)任務(wù)的源信息
*/
publicvoidaddScheduled(Stringname,ScheduledSourcescheduledSource){
if(getRunScheduledName().contains(name)){
thrownewSuperScheduledException("定時(shí)任務(wù)"+name+"已經(jīng)被啟動(dòng)過了");
}
if(!scheduledSource.check()){
thrownewSuperScheduledException("定時(shí)任務(wù)"+name+"源數(shù)據(jù)內(nèi)容錯(cuò)誤");
}

scheduledSource.refreshType();

Runnablerunnable=superScheduledConfig.getRunnable(name);
ThreadPoolTaskSchedulertaskScheduler=superScheduledConfig.getTaskScheduler();


ScheduledFutureschedule=ScheduledFutureFactory.create(taskScheduler,scheduledSource,runnable);
logger.info(df.format(LocalDateTime.now())+"任務(wù)"+name+"已經(jīng)啟動(dòng)...");

superScheduledConfig.addScheduledSource(name,scheduledSource);
superScheduledConfig.addScheduledFuture(name,schedule);
}

/**
*以cron類型啟動(dòng)Scheduled
*
*@paramnamescheduled的名稱
*@paramcroncron表達(dá)式
*/
publicvoidaddCronScheduled(Stringname,Stringcron){
ScheduledSourcescheduledSource=newScheduledSource();
scheduledSource.setCron(cron);

addScheduled(name,scheduledSource);
}

/**
*以fixedDelay類型啟動(dòng)Scheduled
*
*@paramnamescheduled的名稱
*@paramfixedDelay上一次執(zhí)行完畢時(shí)間點(diǎn)之后多長時(shí)間再執(zhí)行
*@paraminitialDelay第一次執(zhí)行的延遲時(shí)間
*/
publicvoidaddFixedDelayScheduled(Stringname,LongfixedDelay,Long...initialDelay){
ScheduledSourcescheduledSource=newScheduledSource();
scheduledSource.setFixedDelay(fixedDelay);
if(initialDelay!=null&&initialDelay.length==1){
scheduledSource.setInitialDelay(initialDelay[0]);
}elseif(initialDelay!=null&&initialDelay.length>1){
thrownewSuperScheduledException("第一次執(zhí)行的延遲時(shí)間只能傳入一個(gè)參數(shù)");
}

addScheduled(name,scheduledSource);
}

/**
*以fixedRate類型啟動(dòng)Scheduled
*
*@paramnamescheduled的名稱
*@paramfixedRate上一次開始執(zhí)行之后多長時(shí)間再執(zhí)行
*@paraminitialDelay第一次執(zhí)行的延遲時(shí)間
*/
publicvoidaddFixedRateScheduled(Stringname,LongfixedRate,Long...initialDelay){
ScheduledSourcescheduledSource=newScheduledSource();
scheduledSource.setFixedRate(fixedRate);
if(initialDelay!=null&&initialDelay.length==1){
scheduledSource.setInitialDelay(initialDelay[0]);
}elseif(initialDelay!=null&&initialDelay.length>1){
thrownewSuperScheduledException("第一次執(zhí)行的延遲時(shí)間只能傳入一個(gè)參數(shù)");
}

addScheduled(name,scheduledSource);
}

/**
*手動(dòng)執(zhí)行一次任務(wù)
*
*@paramnamescheduled的名稱
*/
publicvoidrunScheduled(Stringname){
Runnablerunnable=superScheduledConfig.getRunnable(name);
runnable.run();
}
}

2、增強(qiáng)接口實(shí)現(xiàn)

增強(qiáng)器實(shí)現(xiàn)的整體思路與SpringAop的思路一致,實(shí)現(xiàn)沒有Aop復(fù)雜

(1) 增強(qiáng)接口

@Order(Ordered.HIGHEST_PRECEDENCE)
publicinterfaceBaseStrengthen{
/**
*前置強(qiáng)化方法
*
*@parambeanbean實(shí)例(或者是被代理的bean)
*@parammethod執(zhí)行的方法對象
*@paramargs方法參數(shù)
*/
voidbefore(Objectbean,Methodmethod,Object[]args);

/**
*后置強(qiáng)化方法
*出現(xiàn)異常不會(huì)執(zhí)行
*如果未出現(xiàn)異常,在afterFinally方法之后執(zhí)行
*
*@parambeanbean實(shí)例(或者是被代理的bean)
*@parammethod執(zhí)行的方法對象
*@paramargs方法參數(shù)
*/
voidafter(Objectbean,Methodmethod,Object[]args);

/**
*異常強(qiáng)化方法
*
*@parambeanbean實(shí)例(或者是被代理的bean)
*@parammethod執(zhí)行的方法對象
*@paramargs方法參數(shù)
*/
voidexception(Objectbean,Methodmethod,Object[]args);

/**
*Finally強(qiáng)化方法,出現(xiàn)異常也會(huì)執(zhí)行
*
*@parambeanbean實(shí)例(或者是被代理的bean)
*@parammethod執(zhí)行的方法對象
*@paramargs方法參數(shù)
*/
voidafterFinally(Objectbean,Methodmethod,Object[]args);
}

(2) 代理抽象類

publicabstractclassPoint{
/**
*定時(shí)任務(wù)名
*/
privateStringsuperScheduledName;

/**
*抽象的執(zhí)行方法,使用代理實(shí)現(xiàn)
*@paramrunnable定時(shí)任務(wù)執(zhí)行器
*/
publicabstractObjectinvoke(SuperScheduledRunnablerunnable);

/*普通的get/sets省略*/
}

(3) 調(diào)用鏈類

publicclassChain{
privateListlist;
privateintindex=-1;
/**
*索引自增1
*/
publicintincIndex(){
return++index;
}

/**
*索引還原
*/
publicvoidresetIndex(){
this.index=-1;
}
}

(4) cglib動(dòng)態(tài)代理實(shí)現(xiàn)

使用cglib代理增強(qiáng)器,將增強(qiáng)器全部代理成調(diào)用鏈節(jié)點(diǎn)Point

publicclassRunnableBaseInterceptorimplementsMethodInterceptor{
/**
*定時(shí)任務(wù)執(zhí)行器
*/
privateSuperScheduledRunnablerunnable;
/**
*定時(shí)任務(wù)增強(qiáng)類
*/
privateBaseStrengthenstrengthen;

@Override
publicObjectintercept(Objectobj,Methodmethod,Object[]args,MethodProxymethodProxy)throwsThrowable{
Objectresult;
//如果執(zhí)行的是invoke()方法
if("invoke".equals(method.getName())){
//前置強(qiáng)化方法
strengthen.before(obj,method,args);
try{
//調(diào)用執(zhí)行器中的invoke()方法
result=runnable.invoke();
}catch(Exceptione){
//異常強(qiáng)化方法
strengthen.exception(obj,method,args);
thrownewSuperScheduledException(strengthen.getClass()+"中強(qiáng)化執(zhí)行時(shí)發(fā)生錯(cuò)誤",e);
}finally{
//Finally強(qiáng)化方法,出現(xiàn)異常也會(huì)執(zhí)行
strengthen.afterFinally(obj,method,args);
}
//后置強(qiáng)化方法
strengthen.after(obj,method,args);

}else{
//直接執(zhí)行方法
result=methodProxy.invokeSuper(obj,args);
}
returnresult;
}

publicRunnableBaseInterceptor(Objectobject,SuperScheduledRunnablerunnable){
this.runnable=runnable;
if(BaseStrengthen.class.isAssignableFrom(object.getClass())){
this.strengthen=(BaseStrengthen)object;
}else{
thrownewSuperScheduledException(object.getClass()+"對象不是BaseStrengthen類型");
}
}

publicRunnableBaseInterceptor(){

}
}

(5) 定時(shí)任務(wù)執(zhí)行器實(shí)現(xiàn)

publicclassSuperScheduledRunnable{
/**
*原始的方法
*/
privateMethodmethod;
/**
*方法所在的bean
*/
privateObjectbean;
/**
*增強(qiáng)器的調(diào)用鏈
*/
privateChainchain;


publicObjectinvoke(){
Objectresult;
//索引自增1
if(chain.incIndex()==chain.getList().size()){
//調(diào)用鏈中的增強(qiáng)方法已經(jīng)全部執(zhí)行結(jié)束
try{
//調(diào)用鏈索引初始化
chain.resetIndex();
//增強(qiáng)器全部執(zhí)行完畢,執(zhí)行原本的方法
result=method.invoke(bean);
}catch(IllegalAccessException|InvocationTargetExceptione){
thrownewSuperScheduledException(e.getLocalizedMessage());
}
}else{
//獲取被代理后的方法增強(qiáng)器
Pointpoint=chain.getList().get(chain.getIndex());
//執(zhí)行增強(qiáng)器代理
//增強(qiáng)器代理中,會(huì)回調(diào)方法執(zhí)行器,形成調(diào)用鏈,逐一運(yùn)行調(diào)用鏈中的增強(qiáng)器
result=point.invoke(this);
}
returnresult;
}

/*普通的get/sets省略*/
}

(6) 增強(qiáng)器代理邏輯

com.gyx.superscheduled.core.SuperScheduledApplicationRunner類中的代碼片段

//創(chuàng)建執(zhí)行控制器
SuperScheduledRunnablerunnable=newSuperScheduledRunnable();
runnable.setMethod(scheduledSource.getMethod());
runnable.setBean(scheduledSource.getBean());
//用來存放增強(qiáng)器的代理對象
Listpoints=newArrayList<>(baseStrengthenBeanNames.length);
//循環(huán)所有的增強(qiáng)器的beanName
for(StringbaseStrengthenBeanName:baseStrengthenBeanNames){
//獲取增強(qiáng)器的bean對象
ObjectbaseStrengthenBean=applicationContext.getBean(baseStrengthenBeanName);
//將增強(qiáng)器代理成Point節(jié)點(diǎn)
Pointproxy=ProxyUtils.getInstance(Point.class,newRunnableBaseInterceptor(baseStrengthenBean,runnable));
proxy.setSuperScheduledName(name);
//增強(qiáng)器的代理對象緩存到list中
points.add(proxy);
}
//將增強(qiáng)器代理實(shí)例的集合生成調(diào)用鏈
//執(zhí)行控制器中設(shè)置調(diào)用鏈
runnable.setChain(newChain(points));






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 處理器
    +關(guān)注

    關(guān)注

    68

    文章

    19838

    瀏覽量

    234042
  • 控制器
    +關(guān)注

    關(guān)注

    114

    文章

    17038

    瀏覽量

    183445
  • 增強(qiáng)器
    +關(guān)注

    關(guān)注

    1

    文章

    48

    瀏覽量

    8477
  • SpringBoot
    +關(guān)注

    關(guān)注

    0

    文章

    175

    瀏覽量

    356

原文標(biāo)題:SpringBoot 定時(shí)任務(wù)動(dòng)態(tài)管理通用解決方案

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

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    【API】多pk分組&用戶場景&通用定時(shí)任務(wù)api已經(jīng)發(fā)布

    多PK分組、用戶場景和通用定時(shí)任務(wù)接口已經(jīng)發(fā)布。以上OPEN API 用戶/廠商 可免費(fèi)使用。多PK分組:可讓用戶/廠商對不用產(chǎn)品的設(shè)備進(jìn)行歸類管理。接口兼容原有的單PK分組,不同點(diǎn)在于創(chuàng)建多 pk
    發(fā)表于 08-31 13:08

    Linux系統(tǒng)定時(shí)任務(wù)Crond

    Crond是linux系統(tǒng)中用來定期執(zhí)行命令/腳本或指定程序任務(wù)一種服務(wù)或軟件,般情況下,我們安裝完Centos5/6 linux操作系統(tǒng)之后,默認(rèn)便會(huì)啟動(dòng)Crond任務(wù)調(diào)度服務(wù)。
    發(fā)表于 07-05 06:22

    一種可網(wǎng)絡(luò)化管理和配置機(jī)頂盒的網(wǎng)絡(luò)解決方案

    一種可網(wǎng)絡(luò)化管理和配置機(jī)頂盒的網(wǎng)絡(luò)解決方案
    發(fā)表于 05-25 07:10

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

    這兩方式不能動(dòng)態(tài)添加、刪除、啟動(dòng)、停止任務(wù)。 要實(shí)現(xiàn)動(dòng)態(tài)增刪啟停定時(shí)任務(wù)功能,比較廣泛的做法是集成Quartz框架。但是本人的開發(fā)原則是:
    的頭像 發(fā)表于 09-24 09:49 ?3204次閱讀
    <b class='flag-5'>SpringBoot</b>如何實(shí)現(xiàn)<b class='flag-5'>動(dòng)態(tài)</b>增刪啟停<b class='flag-5'>定時(shí)任務(wù)</b>

    Python定時(shí)任務(wù)的實(shí)現(xiàn)方式

    在日常工作中,我們常常會(huì)用到需要周期性執(zhí)行的任務(wù),一種方式是采用 Linux 系統(tǒng)自帶的 crond 結(jié)合命令行實(shí)現(xiàn)。另外一種方式是直接使用Python。接下來整理的是常見的Python定時(shí)任
    的頭像 發(fā)表于 10-08 15:20 ?9258次閱讀

    如何在SpringBoot項(xiàng)目中實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù)

    之前寫過文章記錄怎么在SpringBoot項(xiàng)目中簡單使用定時(shí)任務(wù),不過由于要借助cron表達(dá)式且都提前定義好放在配置文件里,不能在項(xiàng)目運(yùn)行中動(dòng)態(tài)修改任務(wù)執(zhí)行時(shí)間,實(shí)在不太靈活。
    的頭像 發(fā)表于 09-30 11:16 ?1996次閱讀

    說說Spring定時(shí)任務(wù)如何大規(guī)模企業(yè)級運(yùn)用

    定時(shí)任務(wù)是業(yè)務(wù)應(yīng)用開發(fā)中非常普遍存在的場景(如:每分鐘掃描超時(shí)支付的訂單,每小時(shí)清理次數(shù)據(jù)庫歷史數(shù)據(jù),每天統(tǒng)計(jì)前天的數(shù)據(jù)并生成報(bào)表等等),解決方案很多,Spring 框架提供了
    的頭像 發(fā)表于 11-04 09:36 ?894次閱讀

    解析Golang定時(shí)任務(wù)庫gron設(shè)計(jì)和原理

    正巧,最近看到了 gron 這個(gè)開源項(xiàng)目,它是用 Golang 實(shí)現(xiàn)個(gè)并發(fā)安全的定時(shí)任務(wù)庫。實(shí)現(xiàn)非常簡單精巧,代碼量也不多。今天我們就來起結(jié)合源碼看下,怎樣基于 Golang 的
    的頭像 發(fā)表于 12-15 13:57 ?1651次閱讀

    SpringBoot如何實(shí)現(xiàn)定時(shí)任務(wù)(下)

    SpringBoot創(chuàng)建定時(shí)任務(wù)的方式很簡單,主要有兩方式:、基于注解的方式(@Scheduled)二、數(shù)據(jù)庫動(dòng)態(tài)配置。實(shí)際開發(fā)中,第
    的頭像 發(fā)表于 04-07 14:51 ?1407次閱讀
    <b class='flag-5'>SpringBoot</b>如何實(shí)現(xiàn)<b class='flag-5'>定時(shí)任務(wù)</b>(下)

    SpringBoot如何實(shí)現(xiàn)定時(shí)任務(wù)(上)

    SpringBoot創(chuàng)建定時(shí)任務(wù)的方式很簡單,主要有兩方式:、基于注解的方式(@Scheduled)二、數(shù)據(jù)庫動(dòng)態(tài)配置。實(shí)際開發(fā)中,第
    的頭像 發(fā)表于 04-07 14:51 ?1559次閱讀
    <b class='flag-5'>SpringBoot</b>如何實(shí)現(xiàn)<b class='flag-5'>定時(shí)任務(wù)</b>(上)

    Spring Boot中整合兩定時(shí)任務(wù)的方法

    在 Spring + SpringMVC 環(huán)境中,般來說,要實(shí)現(xiàn)定時(shí)任務(wù),我們有兩中方案,一種是使用 Spring 自帶的定時(shí)任務(wù)處理器
    的頭像 發(fā)表于 04-07 14:55 ?1802次閱讀
    Spring Boot中整合兩<b class='flag-5'>種</b><b class='flag-5'>定時(shí)任務(wù)</b>的方法

    如何動(dòng)態(tài)添加修改刪除定時(shí)任務(wù)?

    如何動(dòng)態(tài)添加修改刪除定時(shí)任務(wù)?那么我們起看看具體怎么實(shí)現(xiàn),先看下本節(jié)大綱: (1)思路說明; (2)代碼解析; (3)修改定時(shí)任務(wù)執(zhí)行周期特別說明;
    的頭像 發(fā)表于 04-12 11:06 ?1353次閱讀

    python定時(shí)任務(wù)實(shí)踐

    由于程序需求,監(jiān)測配置變化需要設(shè)置定時(shí)任務(wù),每分鐘執(zhí)行次,對任務(wù)持久化要求不高,不需要時(shí)可以關(guān)閉定時(shí)任務(wù)
    的頭像 發(fā)表于 05-20 17:53 ?1170次閱讀
    python<b class='flag-5'>定時(shí)任務(wù)</b>實(shí)踐

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

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

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

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