编程知识 cdmana.com

6年老猿帶你掌握Spring Boot實現定時任務的動態增删啟停

在spring boot項目中,可以通過@EnableScheduling注解和@Scheduled注解實現定時任務,也可以通過SchedulingConfigurer接口來實現定時任務。但是這兩種方式不能動態添加、删除、啟動、停止任務。

要實現動態增删啟停定時任務功能,比較廣泛的做法是集成Quartz框架。但是本人的開發原則是:在滿足項目需求的情况下,盡量少的依賴其它框架,避免項目過於臃腫和複雜。

查看spring-context這個jar包中org.springframework.scheduling.ScheduledTaskRegistrar這個類的源代碼,發現可以通過改造這個類就能實現動態增删啟停定時任務功能。

6年老猿帶你掌握Spring Boot實現定時任務的動態增删啟停_spring boot

定時任務列錶頁

6年老猿帶你掌握Spring Boot實現定時任務的動態增删啟停_spring_02

定時任務執行日志

添加執行定時任務的線程池配置類

       

@Configuration
public class SchedulingConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
// 定時任務執行線程池核心線程數
taskScheduler. setPoolSize( 4);
taskScheduler. setRemoveOnCancelPolicy( true);
taskScheduler. setThreadNamePrefix( "TaskSchedulerThreadPool-");
return taskScheduler;
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

添加ScheduledFuture的包裝類。ScheduledFuture是ScheduledExecutorService定時任務線程池的執行結果。

       

public final class ScheduledTask {

volatile ScheduledFuture <?> future;

/**
* 取消定時任務
*/
public void cancel() {
ScheduledFuture <?> future = this. future;
if ( future != null) {
future. cancel( true);
}
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

添加Runnable接口實現類,被定時任務線程池調用,用來執行指定bean裏面的方法。

       

public class SchedulingRunnable implements Runnable {

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

private String beanName;

private String methodName;

private String params;

public SchedulingRunnable( String beanName, String methodName) {
this( beanName, methodName, null);
}

public SchedulingRunnable( String beanName, String methodName, String params) {
this. beanName = beanName;
this. methodName = methodName;
this. params = params;
}

@Override
public void run() {
logger. info( "定時任務開始執行 - bean:{},方法:{},參數:{}", beanName, methodName, params);
long startTime = System. currentTimeMillis();

try {
Object target = SpringContextUtils. getBean( beanName);

Method method = 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 ( Exception ex) {
logger. error( String. format( "定時任務執行异常 - bean:%s,方法:%s,參數:%s ", beanName, methodName, params), ex);
}

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

@Override
public boolean equals( Object o) {
if ( this == o) return true;
if ( o == null || getClass() != o. getClass()) return false;
SchedulingRunnable that = ( SchedulingRunnable) o;
if ( params == null) {
return beanName. equals( that. beanName) &&
methodName. equals( that. methodName) &&
that. params == null;
}

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

@Override
public int hashCode() {
if ( params == null) {
return Objects. hash( beanName, methodName);
}

return Objects. hash( beanName, methodName, params);
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.

添加定時任務注册類,用來增加、删除定時任務。

       
@Component
public class CronTaskRegistrar implements DisposableBean {

private final Map < Runnable, ScheduledTask > scheduledTasks = new ConcurrentHashMap <>( 16);

@Autowired
private TaskScheduler taskScheduler;

public TaskScheduler getScheduler() {
return this. taskScheduler;
}

public void addCronTask( Runnable task, String cronExpression) {
addCronTask( new CronTask( task, cronExpression));
}

public void addCronTask( CronTask cronTask) {
if ( cronTask != null) {
Runnable task = cronTask. getRunnable();
if ( this. scheduledTasks. containsKey( task)) {
removeCronTask( task);
}

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

public void removeCronTask( Runnable task) {
ScheduledTask scheduledTask = this. scheduledTasks. remove( task);
if ( scheduledTask != null)
scheduledTask. cancel();
}

public ScheduledTask scheduleCronTask( CronTask cronTask) {
ScheduledTask scheduledTask = new ScheduledTask();
scheduledTask. future = this. taskScheduler. schedule( cronTask. getRunnable(), cronTask. getTrigger());

return scheduledTask;
}


@Override
public void destroy() {
for ( ScheduledTask task : this. scheduledTasks. values()) {
task. cancel();
}

this. scheduledTasks. clear();
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.

添加定時任務示例類

       

@Component( "demoTask")
public class DemoTask {
public void taskWithParams( String params) {
System. out. println( "執行有參示例任務:" + params);
}

public void taskNoParams() {
System. out. println( "執行無參示例任務");
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

定時任務數據庫錶設計

6年老猿帶你掌握Spring Boot實現定時任務的動態增删啟停_java_03

定時任務數據庫錶設計

添加定時任務實體類

       

public class SysJobPO {
/**
* 任務ID
*/
private Integer jobId;
/**
* bean名稱
*/
private String beanName;
/**
* 方法名稱
*/
private String methodName;
/**
* 方法參數
*/
private String methodParams;
/**
* cron錶達式
*/
private String cronExpression;
/**
* 狀態(1正常 0暫停)
*/
private Integer jobStatus;
/**
* 備注
*/
private String remark;
/**
* 創建時間
*/
private Date createTime;
/**
* 更新時間
*/
private Date updateTime;

public Integer getJobId() {
return jobId;
}

public void setJobId( Integer jobId) {
this. jobId = jobId;
}

public String getBeanName() {
return beanName;
}

public void setBeanName( String beanName) {
this. beanName = beanName;
}

public String getMethodName() {
return methodName;
}

public void setMethodName( String methodName) {
this. methodName = methodName;
}

public String getMethodParams() {
return methodParams;
}

public void setMethodParams( String methodParams) {
this. methodParams = methodParams;
}

public String getCronExpression() {
return cronExpression;
}

public void setCronExpression( String cronExpression) {
this. cronExpression = cronExpression;
}

public Integer getJobStatus() {
return jobStatus;
}

public void setJobStatus( Integer jobStatus) {
this. jobStatus = jobStatus;
}

public String getRemark() {
return remark;
}

public void setRemark( String remark) {
this. remark = remark;
}

public Date getCreateTime() {
return createTime;
}

public void setCreateTime( Date createTime) {
this. createTime = createTime;
}

public Date getUpdateTime() {
return updateTime;
}

public void setUpdateTime( Date updateTime) {
this. updateTime = updateTime;
}

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.

新增定時任務

6年老猿帶你掌握Spring Boot實現定時任務的動態增删啟停_spring_04

新增定時任務

       

boolean success = sysJobRepository. addSysJob( sysJob);
if ( ! success)
return OperationResUtils. fail( "新增失敗");
else {
if ( sysJob. getJobStatus(). equals( SysJobStatus. NORMAL. ordinal())) {
SchedulingRunnable task = new SchedulingRunnable( sysJob. getBeanName(), sysJob. getMethodName(), sysJob. getMethodParams());
cronTaskRegistrar. addCronTask( task, sysJob. getCronExpression());
}
}

return OperationResUtils. success();
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

修改定時任務,先移除原來的任務,再啟動新任務

       

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

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

return OperationResUtils. success();
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

删除定時任務

       

boolean success = sysJobRepository. deleteSysJobById( req. getJobId());
if ( ! success)
return OperationResUtils. fail( "删除失敗");
else{
if ( existedSysJob. getJobStatus(). equals( SysJobStatus. NORMAL. ordinal())) {
SchedulingRunnable task = new SchedulingRunnable( existedSysJob. getBeanName(), existedSysJob. getMethodName(), existedSysJob. getMethodParams());
cronTaskRegistrar. removeCronTask( task);
}
}

return OperationResUtils. success();
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

定時任務啟動/停止狀態切換

       
if (existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())) {
SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
cronTaskRegistrar.addCronTask(task, existedSysJob.getCronExpression());
} else {
SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
cronTaskRegistrar.removeCronTask(task);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

添加實現了CommandLineRunner接口的SysJobRunner類,當spring boot項目啟動完成後,加載數據庫裏狀態為正常的定時任務。

       
@Service
public class SysJobRunner implements CommandLineRunner {

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

@Autowired
private ISysJobRepository sysJobRepository;

@Autowired
private CronTaskRegistrar cronTaskRegistrar;

@Override
public void run( String... args) {
// 初始加載數據庫裏狀態為正常的定時任務
List < SysJobPO > jobList = sysJobRepository. getSysJobListByStatus( SysJobStatus. NORMAL. ordinal());
if ( CollectionUtils. isNotEmpty( jobList)) {
for ( SysJobPO job : jobList) {
SchedulingRunnable task = new SchedulingRunnable( job. getBeanName(), job. getMethodName(), job. getMethodParams());
cronTaskRegistrar. addCronTask( task, job. getCronExpression());
}

logger. info( "定時任務已加載完畢...");
}
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

工具類SpringContextUtils,用來從spring容器裏獲取bean

       
@Component
public class SpringContextUtils implements ApplicationContextAware {

private static ApplicationContext applicationContext;

@Override
public void setApplicationContext( ApplicationContext applicationContext)
throws BeansException {
SpringContextUtils. applicationContext = applicationContext;
}

public static Object getBean( String name) {
return applicationContext. getBean( name);
}

public static < T > T getBean( Class < T > requiredType) {
return applicationContext. getBean( requiredType);
}

public static < T > T getBean( String name, Class < T > requiredType) {
return applicationContext. getBean( name, requiredType);
}

public static boolean containsBean( String name) {
return applicationContext. containsBean( name);
}

public static boolean isSingleton( String name) {
return applicationContext. isSingleton( name);
}

public static Class <? extends Object > getType( String name) {
return applicationContext. getType( name);
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.


版权声明
本文为[一點唐城]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/11/20211125173308748a.html

Scroll to Top