上一篇文章介绍了基于SchedulingConfigurer实现定时任务实时配置cron表达式和开关,存在一个问题:修改完cron表达式后不能立刻生效,需要等到下一次定时任务执行后才可以生效。本文更换实现思路,从ThreadPoolTaskScheduler类入手,解决这个问题。
核心类
@Slf4j
@Component
public class MyScheduleComponent {
@Resource
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Resource
private SysConfigDao sysConfigDao;
@Autowired
private TaskService taskService;
private ScheduledFuture<?> future;
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler(){
return new ThreadPoolTaskScheduler();
}
/**
* 初始化定时任务,确保项目启动的时候会根据配置自动执行
*/
@PostConstruct
public void initScheduler(){
// 先判断数据库保存的开关状态,如果开关为off则不执行定时任务
String status = sysConfigDao.getValueByKey(ScheduleConstant.STATUS_KEY);
if(StringUtils.isEmpty(status) || !ScheduleConstant.DEFAULT_STATUS.equals(status)){
return;
}
String cron = sysConfigDao.getValueByKey(ScheduleConstant.CRON_KEY);
log.info("【定时任务】cron配置:{}", cron);
if(StringUtils.isEmpty(cron)){
log.info("【定时任务】缺少cron配置,将使用默认配置,每10秒执行一次");
cron = ScheduleConstant.DEFAULT_CRON;
}
startCron(cron);
}
/**
* 开启定时任务
* @param cron
*/
public void startCron(String cron){
// 先关闭
stopCron();
future = threadPoolTaskScheduler.schedule(()->{
log.info("【定时任务】任务开始执行");
String status = sysConfigDao.getValueByKey(ScheduleConstant.STATUS_KEY);
log.info("【定时任务】开关配置:{}", status);
if(StringUtils.isEmpty(status)){
log.info("【定时任务】缺少开关配置,将使用默认配置:on");
status = ScheduleConstant.DEFAULT_STATUS;
}
if("on".equals(status)){
// 执行业务
taskService.doTask();
}
}, new CronTrigger(cron));
}
/**
* 关闭定时任务
*/
public void stopCron(){
log.info("【定时任务】定时任务关闭");
if(future != null){
future.cancel(true);
}
}
/**
* 修改cron表达式,一经修改立即生效
* @param cron
*/
public void changeCron(String cron){
log.info("【定时任务】修改定时任务,cron = {}", cron);
startCron(cron);
}
}
控制器代码
@RestController
public class ScheduleController {
@Resource
private SysConfigDao sysConfigDao;
@Resource
private MyScheduleComponent mySchedule;
/**
* 获取配置信息
* @return
*/
@GetMapping("config")
public Map<String, String> config(){
String cron = sysConfigDao.getValueByKey(ScheduleConstant.CRON_KEY);
String status = sysConfigDao.getValueByKey(ScheduleConstant.STATUS_KEY);
Map<String, String> result = new HashMap<>();
result.put("cron", cron);
result.put("status", status);
return result;
}
/**
* 修改表达式
* @param cron
* @return
*/
@PutMapping("cron")
public String cron(@RequestParam(value = "cron", required = true) String cron){
// TODO cron表达式做格式校验
// 先修改数据库
sysConfigDao.updateValueByKey(ScheduleConstant.CRON_KEY, cron);
// 再修改cron
mySchedule.changeCron(cron);
return "success";
}
/**
* 修改开关状态
* @param status
* @return
*/
@PutMapping("status")
public String status(@RequestParam(value = "status", required = true)String status){
// TODO status做格式校验
sysConfigDao.updateValueByKey(ScheduleConstant.STATUS_KEY, status);
mySchedule.stopCron();
return "success";
}
}
测试结果
项目启动时定时任务已经生效:
用postman测试修改cron表达式,从10秒执行一次修改为30秒执行一次:
日志输出如下:
从日志可以看到,定时任务立刻生效了。
用postman测试关闭定时任务:
日志输出如下,定时任务成功关闭。
最后附上代码:
定时任务实时配置cron表达式和开关(升级版)示例代码