简易定时任务管理器开发
定时任务执行在项目中经常需要使用,springboot 提供了简单易用的定时任务,通过@Scheduled标签和@EnableScheduling就能实现。
这里通过自定义一个简易的任务调度管理器,来实现简易的自定义任务调度。
定义一个定时任务调度的starter:
设计思路如下:

定时任务使用 ThreadPoolTaskScheduler来执行定时任务。通过定时任务执行返回的 ScheduledFuture可以停止任务的执行。
Scheduler Starter 的开发主要类如下:
1. 定时任务执行类:
接口:
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; public interface CronTask { void start(); void stop(); void reStart(); String getCron(); void setCron(String corn); String getStatus(); void setStatus(String status); void setSchedulerPool(ThreadPoolTaskScheduler schedulerPool); }
实现类
public class CronTaskService implements CronTask { private static final Logger logger = LoggerFactory.getLogger(CronTaskService.class); private ThreadPoolTaskScheduler taskScheduler; private ScheduledFuture<?> future; private ScheduledJob scheduledJob; private int taskId; private String status; private String currentCron; public CronTaskService( int taskId, String cron, ScheduledJob job, ThreadPoolTaskScheduler schedulerPool, String status) { this.taskId = taskId; this.currentCron = cron; this.scheduledJob = job; this.status = status; if (schedulerPool == null) { this.taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.initialize(); } else { this.taskScheduler = schedulerPool; } } @Override public void start() { if (future != null) { future.cancel(true); } if (SchedulerConstant.TASK_STATUS_INACTIVE.equals(this.status)) { logger.info( "Task id " + taskId + " is configured as inactive currently. It won't be started automatically. Please try to manually start the job if needed."); return; } future = taskScheduler.schedule( scheduledJob, (triggerContext) -> new CronTrigger(currentCron).nextExecutionTime(triggerContext)); logger.info("Task " + taskId + " is started."); } @Override public void stop() { if (future != null) { future.cancel(false); future = null; logger.info("Task " + taskId + " is stopped successfully."); } } @Override public void reStart() { stop(); start(); } @Override public String getCron() { return this.currentCron; } @Override public void setCron(String corn) { this.currentCron = corn; } @Override public void setSchedulerPool(ThreadPoolTaskScheduler schedulerPool) { this.taskScheduler = schedulerPool; } @Override public String getStatus() { return status; } @Override public void setStatus(String status) { this.status = status; } }
2. 定时任务调度管理器
public interface ScheduledTaskManager {
void initScheduledJobs();
boolean startTask(int taskId);
boolean stopTask(int taskId);
boolean reScheduleTask(int taskId, String cron) throws Exception;
boolean reScheduleAllTask(String cron) throws Exception;
List<TaskDefinition> getAllTasks();
boolean registerTask(TaskDefinition taskDefinition) throws Exception;
boolean deleteTask(int taskId) throws Exception;
boolean updateTask(TaskDefinition taskDefinition) throws Exception;
}
@Service public class ScheduledTaskManagerService implements ScheduledTaskManager { { @Autowired ThreadPoolTaskScheduler taskScheduler; @Autowired TaskDefHandler taskDefinitionService; @Autowired ApplicationContextUtils applicationContextUtils; private Map<Integer, DynamicCronTask> scheduledJobs = new ConcurrentHashMap<>(); private static final Logger logger = LoggerFactory.getLogger(ScheduledTaskManagerService.class); @PostConstruct @Override public void initScheduledJobs() { List<TaskDefinition> taskDefinitions = taskDefinitionService.getAllTaskDefs(); logger.info("There are " + taskDefinitions.size() + " tasks which will start to run."); taskDefinitions.forEach( taskDefinition -> { try { CronTask dynamicCronTask = createCronTask(taskDefinition); dynamicCronTask.start(); } catch (ClassNotFoundException | NoSuchMethodException e) { logger.error( "Task " + taskDefinition.getTaskId() + " start failed with error: Job class is not found. "); } catch (Exception e) { logger.error( "Task " + taskDefinition.getTaskId() + " start failed with error: " + e.getLocalizedMessage()); } }); } private CronTask createCronTask(TaskDefinition taskDefinition) throws ClassNotFoundException, NoSuchMethodException { Class<ScheduledJob> scheduledJobClass = (Class<ScheduledJob>) Class.forName(taskDefinition.getJobClass()); applicationContextUtils.registerBean(scheduledJobClass.getName(), scheduledJobClass); ScheduledJob instance = (ScheduledJob) applicationContextUtils.getBean(scheduledJobClass.getName()); CronTask dynamicCronTask = new CronTaskService( taskDefinition.getTaskId(), taskDefinition.getCron(), instance, taskScheduler, taskDefinition.getStatus()); scheduledJobs.put(taskDefinition.getTaskId(), dynamicCronTask); return dynamicCronTask; } @Override public boolean startTask(int taskId) { TaskDefinition task = taskDefinitionService.getTaskDefById(taskId); if (task == null) { logger.error("Task id " + taskId + " is not existing in the system. It can't be started."); return false; } if (SchedulerConstant.TASK_STATUS_INACTIVE.equals(task.getStatus())) { task.setStatus(SchedulerConstant.TASK_STATUS_ACTIVE); taskDefinitionService.updateTaskDefinition(task); } if (scheduledJobs.containsKey(taskId)) { CronTask dynamicCronTask = scheduledJobs.get(taskId); dynamicCronTask.setStatus(SchedulerConstant.TASK_STATUS_ACTIVE);
dynamicCronTask.setCron(task.getCron()); dynamicCronTask.start(); return true; } try { CronTask dynamicCronTask = createDynamicCronTask(task); dynamicCronTask.setStatus(SchedulerConstant.TASK_STATUS_ACTIVE); dynamicCronTask.start(); return true; } catch (ClassNotFoundException e) { logger.error("Task " + taskId + " start failed with error: Job class is not found. "); } catch (Exception e) { logger.error("Task " + taskId + " start failed with error: " + e.getLocalizedMessage()); } return false; } @Override public boolean stopTask(int taskId) { TaskDefinition task = taskDefinitionService.getTaskDefById(taskId); if (task == null) { logger.error("Task id " + taskId + " is not existing in the system. It can't be stopped."); return false; } if (SchedulerConstant.TASK_STATUS_ACTIVE.equals(task.getStatus())) { task.setStatus(SchedulerConstant.TASK_STATUS_INACTIVE); taskDefinitionService.updateTaskDefinition(task); } if (scheduledJobs.containsKey(taskId)) { CronTask dynamicCronTask = scheduledJobs.get(taskId); dynamicCronTask.setStatus(SchedulerConstant.TASK_STATUS_INACTIVE); dynamicCronTask.stop(); } return true; } @Override public boolean reScheduleTask(int taskId, String cron) throws Exception { if (!CommonValidateUtils.isCronValid(cron)) { logger.error( "Setting the task id " + taskId + " with cron expression is wrong, please correct it"); throw new Exception("Cron expression is wrong, please correct it"); } TaskDefinition taskDefinition = taskDefinitionService.getTaskDefById(taskId); if (taskDefinition == null) { logger.error("Task id " + taskId + " item is not found, it can't be rescheduled."); throw new Exception("Task id " + taskId + " item is not found, it can't be rescheduled."); } taskDefinition.setCron(cron); boolean result = taskDefinitionService.updateTaskDefinition(taskDefinition); if (!result) { logger.error("Update Task id " + taskId + " failed."); throw new Exception("Update Task id " + taskId + " failed."); } if (scheduledJobs.containsKey(taskId)) { DynamicCronTask dynamicCronTask = scheduledJobs.get(taskId); if (!dynamicCronTask.getCron().equals(cron)) { dynamicCronTask.setCron(cron); dynamicCronTask.reStart(); } } return true; } @Override public boolean reScheduleAllTask(String cron) throws Exception { if (!CommonValidateUtils.isCronValid(cron)) { logger.error("Reschedule all the task with incorrect cron expression."); throw new Exception("Cron expression is wrong, please correct it"); } List<TaskDefinition> allTasks = getAllTasks(); for (TaskDefinition taskDef : allTasks) { taskDef.setCron(cron); reScheduleTask(taskDef.getTaskId(), cron); } return true; } @Override public List<TaskDefinition> getAllTasks() { return taskDefinitionService.getAllTaskDefs(); } @Override public boolean registerTask(TaskDefinition taskDefinition) throws Exception { TaskDefinition task = taskDefinitionService.getTaskDefById(taskDefinition.getTaskId()); if (task != null) { logger.error( "Register the task with id " + taskDefinition.getTaskId() + " is already existing. Can't register the task."); throw new Exception( "Same task id " + taskDefinition.getTaskId() + " is already existing. Please use another one."); } return taskDefinitionService.registerTaskDef(taskDefinition); } @Override public boolean deleteTask(int taskId) throws Exception { TaskDefinition task = taskDefinitionService.getTaskDefById(taskId); if (task == null) { throw new Exception("Task id " + taskId + " item is not found, it can't be deleted."); } if (scheduledJobs.containsKey(taskId)) { CronTask dynamicCronTask = scheduledJobs.get(taskId); dynamicCronTask.setStatus(SchedulerConstant.TASK_STATUS_INACTIVE); dynamicCronTask.stop(); scheduledJobs.remove(taskId); } return taskDefinitionService.deleteTaskDefById(taskId); } @Override public boolean updateTask(TaskDefinition taskDefinition) throws Exception { TaskDefinition task = taskDefinitionService.getTaskDefById(taskDefinition.getTaskId()); if (task == null) { throw new Exception( "Task id " + taskDefinition.getTaskId() + " item is not found, it can't be updated."); } boolean result = taskDefinitionService.updateTaskDefinition(taskDefinition); if (!result) { logger.error("Update Task id " + taskDefinition.getTaskId() + " failed."); throw new Exception("Update Task id " + taskDefinition.getTaskId() + " failed."); } return true; } }
3. 数据库操作类
public interface TaskDefHandler { List<TaskDefinition> getAllTaskDefs(); boolean updateTaskDefinition(TaskDefinition task); TaskDefinition getTaskDefById(int taskId); boolean deleteTaskDefById(int taskId); boolean registerTaskDef(TaskDefinition task); }
6. 定时任务定义实体类
public class TaskDefinition { private Integer taskId; private String cron; private String taskName; private String description; private String jobClass; private String status; private String lockInfo; public Integer getTaskId() { return taskId; } public void setTaskId(Integer taskId) { this.taskId = taskId; } public String getCron() { return cron; } public void setCron(String cron) { this.cron = cron; } public String getTaskName() { return taskName; } public void setTaskName(String taskName) { this.taskName = taskName; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getJobClass() { return jobClass; } public void setJobClass(String jobClass) { this.jobClass = jobClass; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getLockInfo() { return lockInfo; } public void setLockInfo(String lockInfo) { this.lockInfo = lockInfo; } }
5. 定时任务抽象类
public abstract class ScheduledJob implements Runnable { @Override public void run() { boolean flag = preProcess(); if (flag) { execute(); postProcess(); } } public abstract void execute(); private boolean preProcess() { return true; } private void postProcess() {} }
6. 定时任务配置类
import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "schedule") public class ScheduleProperties { private int poolSize = 1; public int getPoolSize() { return poolSize; } public void setPoolSize(int poolSize) { if(poolSize <= 0) { poolSize = 1; } this.poolSize = poolSize; } }
7. RestAPI 控制器
@RestController @RequestMapping("/scheduler") public class SchedulerController { @Autowired ScheduledTaskManager scheduledTaskManager; @PutMapping("/reSchedule") public RestResponse reSchedule( @RequestParam(name = "taskId") int taskId, @RequestParam(name = "cron") String cron) { RestResponse resp = new RestResponse(); if (!CommonValidateUtils.isCronValid(cron)) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage("Bad Cron expression, please correct it and try again."); return resp; } try { scheduledTaskManager.reScheduleTask(taskId, cron); } catch (Exception e) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage(e.getMessage()); return resp; } resp.setMessage("Reschedule the task id " + taskId + " successfully."); return resp; } @PutMapping("/stop") public RestResponse stop(@RequestParam(name = "taskId") int taskId) { RestResponse resp = new RestResponse(); boolean result = scheduledTaskManager.stopTask(taskId); if (result) { resp.setMessage("Stop the task id " + taskId + " successfully."); } else { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage( "Stop the task id " + taskId + " failed. Please check if the task id is correct or please try it again."); } return resp; } @PutMapping("/start") public RestResponse start(@RequestParam(name = "taskId") int taskId) { RestResponse resp = new RestResponse(); boolean result = scheduledTaskManager.startTask(taskId); if (result) { resp.setMessage("Start the task id " + taskId + " successfully."); } else { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage( "Start the task id " + taskId + " failed. Please check if the task id is existing or please try it again."); } return resp; } @PutMapping("/reScheduleAll") public RestResponse reScheduleAll(@RequestParam(name = "cron") String cron) { RestResponse resp = new RestResponse(); if (!CommonValidateUtils.isCronValid(cron)) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage("Bad Cron expression, please correct it and try again."); return resp; } try { scheduledTaskManager.reScheduleAllTask(cron); resp.setMessage("Reschedule all the tasks successfully."); } catch (Exception e) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage("Reschedule some tasks are failed. Please check the error in log."); } return resp; } @GetMapping("/getAllTasks") public List<TaskDefinition> getAllTasks() { return scheduledTaskManager.getAllTasks(); } @PostMapping("/registerTask") public RestResponse registerTask(@RequestBody TaskDefinition taskDefinition) { RestResponse resp = new RestResponse(); if (!checkRequiredFields(taskDefinition)) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage( "At least one of the required fields taskId, cron, jobClass are missing the value."); return resp; } if (!CommonValidateUtils.isCronValid(taskDefinition.getCron())) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage("Bad Cron expression, please correct it and try again."); return resp; } try { checkJobClass(taskDefinition.getJobClass()); } catch (Exception e) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage("Job class provided is neither existing nor defined correctly."); return resp; } taskDefinition.setStatus(SchedulerConstant.TASK_STATUS_ACTIVE); boolean result; try { result = scheduledTaskManager.registerTask(taskDefinition); if (!result) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage("Register task failed, please try again."); } else { resp.setMessage("Register task successfully."); } } catch (Exception e) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage(e.getLocalizedMessage()); } return resp; } @PutMapping("updateTask") public RestResponse updateTask(@RequestBody TaskDefinition taskDefinition) { RestResponse resp = new RestResponse(); if (!checkRequiredFields(taskDefinition)) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage( "At least one of the required fields taskId, cron, jobClass are missing the value."); return resp; } if (!CommonValidateUtils.isCronValid(taskDefinition.getCron())) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage("Bad Cron expression, please correct it and try again."); return resp; } try { checkJobClass(taskDefinition.getJobClass()); } catch (Exception e) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage("Job class provided is neither existing nor defined correctly."); return resp; } boolean result; try { result = scheduledTaskManager.updateTask(taskDefinition); if (!result) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage("Update task failed, please try again."); } else { resp.setMessage("Update task successfully."); } } catch (Exception e) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage(e.getLocalizedMessage()); } return resp; } @DeleteMapping("deleteTask") public RestResponse deleteTask(@RequestParam(name = "taskId") int taskId) { RestResponse resp = new RestResponse(); try { boolean result = scheduledTaskManager.deleteTask(taskId); if (!result) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage("Delete task failed, please try again."); } else { resp.setMessage("Delete task successfully."); } } catch (Exception e) { resp.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); resp.setMessage("Delete task failed. Error : " + e.getLocalizedMessage()); } return resp; } private boolean checkRequiredFields(TaskDefinition taskDefinition) { return taskDefinition.getTaskId() != null && !isEmpty(taskDefinition.getCron()) && !isEmpty(taskDefinition.getJobClass()); } private boolean isEmpty(String cron) { return cron == null || "".equals(cron.trim()); } private void checkJobClass(String jobClass) throws Exception { Class clazz = Class.forName(jobClass); Class supperClass = clazz.getSuperclass(); if (supperClass != ScheduledJob.class) { throw new Exception("Class provided is not correct."); } } }
8. 自动配置类
@Configuration @EnableConfigurationProperties(ScheduleProperties.class) @ConditionalOnProperty(prefix = "schedule", name = "enabled", havingValue = "true" , matchIfMissing = true) public class TaskConfigure { @Autowired private ScheduleProperties scheduleProperties; @Bean ThreadPoolTaskScheduler jobScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(scheduleProperties.getPoolSize()); return taskScheduler; } }
9. 在resources下新建文件夹META-INF, 新建文件spring.factories, 添加如下语句
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.demo.schedule.config.TaskConfigure

浙公网安备 33010602011771号