需求

项目中需要用到一些简单的定时任务,定时执行一些事先写好的java方法
但具体何时执行等,需要做到数据库,实现动态配置
最终选择采用Springboot自带的方案 @ScheduleSchedulingConfigurer 来实现

参考

参考以下文章
https://www.cnblogs.com/mmzs/p/10161936.html
https://www.cnblogs.com/zt007/p/8954096.html

数据库

代码

主要依赖 Springboot2 MybatisPLus


定时任务方法类
Job.java

  1. /**
  2. * 定时任务
  3. * zmh
  4. * 2019-5-7 13:54:08
  5. */
  6. public class Job {
  7. public void test5(String str){
  8. System.out.println("定时任务 5秒执行一次 参数:" + str);
  9. }
  10. public void test12(){
  11. System.out.println("定时任务 12点执行一次 无参数");
  12. }
  13. }


配置定时任务
ScheduleConfig.java

  1. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  2. import com.xxx.xxx.entity.QuarztJob;
  3. import com.xxx.xxx.service.IQuarztJobService;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.scheduling.Trigger;
  9. import org.springframework.scheduling.TriggerContext;
  10. import org.springframework.scheduling.annotation.EnableScheduling;
  11. import org.springframework.scheduling.annotation.SchedulingConfigurer;
  12. import org.springframework.scheduling.config.ScheduledTaskRegistrar;
  13. import org.springframework.scheduling.support.CronTrigger;
  14. import org.springframework.stereotype.Component;
  15. import java.util.Date;
  16. import java.util.List;
  17. import java.util.Map;
  18. /**
  19. * zzzmh
  20. * 2019-5-6 16:29:56
  21. * 动态定时任务
  22. */
  23. @Configuration
  24. @EnableScheduling
  25. public class ScheduleConfig implements SchedulingConfigurer {
  26. @Autowired
  27. private IQuarztJobService quarztJobService;
  28. /**
  29. * 注册定时任务
  30. *
  31. * 程序启动先获取所有任务id,并逐一查询出最新状态和执行时间
  32. * 如果状态status = 1说明是暂停中,跳过执行,但依旧注册
  33. * 每次执行前查看数据库最新的任务状态 任务目标和参数
  34. * 每次执行后查看数据库注册最新的下次执行时间
  35. */
  36. @Override
  37. public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
  38. QueryWrapper<QuarztJob> wrapper = new QueryWrapper<>();
  39. // 查询所有任务id (这一步先不管 status)
  40. List<Map<String, Object>> list = quarztJobService.listMaps(wrapper.select("id"));
  41. // 根据数据库配置实时触发
  42. for (Map<String, Object> map : list) {
  43. int id = (int) map.get("id");
  44. taskRegistrar.addTriggerTask(
  45. // 添加任务内容(Runnable)
  46. () -> {
  47. // 从数据库获取执行任务 每次要确保拿最新的
  48. QuarztJob job = quarztJobService.getById(id);
  49. // 如果 status 是1 代表暂停 跳过任务
  50. if (job.getStatus().intValue() == 0) {
  51. try {
  52. // 这里通过反射调用方法
  53. // 包名根据实际Bean所在位置调整
  54. String pack = "com.xxx.xxx.quarztJob.";
  55. Class<?> obj = Class.forName(pack + job.getBeanName());
  56. // 考虑程序复杂度,简化为根据数据库参数字段是否为空 要么无参数 要么单String类型参数
  57. String params = job.getParams();
  58. if (StringUtils.isBlank(params)) {
  59. Method method = obj.getMethod(job.getMethodName());
  60. // 每次new一个新对象
  61. method.invoke(obj.newInstance());
  62. } else {
  63. Method method = obj.getMethod(job.getMethodName(), String.class);
  64. // 每次new一个新对象
  65. method.invoke(obj.newInstance(), params);
  66. }
  67. } catch (Exception e) {
  68. e.printStackTrace();
  69. }
  70. }
  71. },
  72. // 设置执行周期(Trigger)
  73. triggerContext -> {
  74. // 从数据库获取执行周期 每次要确保拿最新的
  75. QuarztJob job = quarztJobService.getById(id);
  76. String cron = job.getCronExpression();
  77. // 返回执行周期(Date)
  78. return new CronTrigger(cron).nextExecutionTime(triggerContext);
  79. }
  80. );
  81. }
  82. }
  83. }

运行结果

需要注意:由于用了反射方法,定时任务数据库和实体类之间的配置必须一一对应,否则会一触发就报错!

另外直接在Job中使用@Autowired 存在无法注入Service问题
目前我用的解决方案是从注册定时任务的地方传参

END

欢迎关注
个人博客: https://zzzmh.cn
学习笔记: https://leanote.zzzmh.cn
极简壁纸: https://bz.zzzmh.cn
极简插件: https://chrome.zzzmh.cn