In the spring boot project, you can implement Scheduled tasks through the @ enableshcheduling annotation and @ Scheduled annotation, or through the scheduleconfigurer interface. However, these two methods cannot dynamically add, delete, start and stop tasks.
To realize the function of dynamic addition and deletion of start and stop scheduled tasks, a more extensive approach is to integrate the Quartz framework. However, my development principle is: under the condition of meeting the needs of the project, rely on other frameworks as little as possible to avoid the project being too cumbersome and complex.
Check the source code of org.springframework.scheduling.scheduledtaskeregistrar in the jar package spring context. It is found that the function of dynamically adding and deleting scheduled tasks can be realized by modifying this class.
Scheduled task list page
Scheduled task execution log
Add a thread pool configuration class that executes scheduled tasks
@Configuration public class SchedulingConfig { @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); // Number of scheduled task execution thread pool core threads taskScheduler.setPoolSize(4); taskScheduler.setRemoveOnCancelPolicy(true); taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-"); return taskScheduler; } }
Add a wrapper class for ScheduledFuture. ScheduledFuture is the execution result of the ScheduledExecutorService scheduled task thread pool.
public final class ScheduledTask { volatile ScheduledFuture<?> future; /** * Cancel scheduled task */ public void cancel() { ScheduledFuture<?> future = this.future; if (future != null) { future.cancel(true); } } }
Add the Runnable interface implementation class, which is called by the scheduled task thread pool to execute the methods in the specified 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("Scheduled task start execution - bean: {},method:{},Parameters:{}", 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("Scheduled task execution exception - bean: %s,method:%s,Parameters:%s ", beanName, methodName, params), ex); } long times = System.currentTimeMillis() - startTime; logger.info("Scheduled task execution end - bean: {},method:{},Parameters:{},Time consuming:{} millisecond", 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); } }
Add a scheduled task registration class to add and delete scheduled tasks.
@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(); } }
Add scheduled task sample class
@Component("demoTask") public class DemoTask { public void taskWithParams(String params) { System.out.println("Perform a sample task with parameters:" + params); } public void taskNoParams() { System.out.println("Perform the parameterless sample task"); } }
Table design of scheduled task database
Table design of scheduled task database
Add scheduled task entity class
public class SysJobPO { /** * Task ID */ private Integer jobId; /** * bean name */ private String beanName; /** * Method name */ private String methodName; /** * Method parameters */ private String methodParams; /** * cron expression */ private String cronExpression; /** * Status (1) normal 0 (pause) */ private Integer jobStatus; /** * remarks */ private String remark; /** * Creation time */ private Date createTime; /** * Update time */ 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; } }
Add scheduled task
Add scheduled task
boolean success = sysJobRepository.addSysJob(sysJob); if (!success) return OperationResUtils.fail("Failed to add"); 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();
To modify a scheduled task, first remove the original task and then start a new task
boolean success = sysJobRepository.editSysJob(sysJob); if (!success) return OperationResUtils.fail("Edit failed"); else { //Remove before adding 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();
Delete scheduled task
boolean success = sysJobRepository.deleteSysJobById(req.getJobId()); if (!success) return OperationResUtils.fail("Deletion failed"); else{ if (existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal())) { SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams()); cronTaskRegistrar.removeCronTask(task); } } return OperationResUtils.success();
Scheduled task start / stop state switching
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); }
Add the SysJobRunner class that implements the CommandLineRunner interface. After the spring boot project is started, load the scheduled tasks in the database that are in normal status. In addition, official account of the public number ape technology column, reply keywords 9527, send you a Spring Cloud Alibaba video tutorial!
@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) { // Initially load the scheduled tasks with normal status in the database 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("The scheduled task has been loaded..."); } } }
The tool class SpringContextUtils is used to obtain bean s from the spring container
@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); } }
At the end of this article, refer to the code in this article, which can be run successfully and tested in person!