• jwbasta-Springboot集成Quartz


    功能点

    1. 添加CRON、固定间隔定时
    2. 修改定时的触发器
    3. 修改定时参数
    4. 暂停定时
    5. 启动暂停的定时
    6. 获取所有定时
    7. 启动所有定时
    8. 停止定时

    0.通过maven引入jar包

    1. <dependency>
    2. <groupId>org.springframework.bootgroupId>
    3. <artifactId>spring-boot-starter-quartzartifactId>
    4. dependency>

    1. 定时类型

    Quartz可以通过org.quartz.Trigger.class去拓展定时类型的,目前需求需要支持CRON和固定间隔两种定时类型,后期需要支持跳过节假日、周一~周五、周末等需求,所以需要让定时类型支持拓展。

    1. 1.1定时类型
    2. /**
    3. *
    4. * 定时类型触发器类型
    5. * @author jiangwei
    6. *
    7. */
    8. public enum TriggerType {
    9. /**
    10. * 标准CRON支持
    11. */
    12. CRON("标准CRON支持"),
    13. /**
    14. * 固定间隔毫秒
    15. */
    16. INTERVAL_MILLISECOND("固定间隔毫秒"),
    17. /**
    18. * 固定间隔秒
    19. */
    20. INTERVAL_SECOND("固定间隔秒"),
    21. /**
    22. * 固定间隔分钟
    23. */
    24. INTERVAL_MINUTE("固定间隔分钟"),
    25. /**
    26. * 固定间隔小时
    27. */
    28. INTERVAL_HOUR("固定间隔小时"),
    29. /**
    30. * 工作日,跳过节假日
    31. */
    32. WEEKDAYS("工作日,跳过节假日"),
    33. /**
    34. * 节假日
    35. */
    36. HOLIDAY("节假日")
    37. ;
    38. private String describe;
    39. TriggerType(String describe) {
    40. this.describe = describe;
    41. }
    42. public String getDescribe() {
    43. return describe;
    44. }
    45. public void setDescribe(String describe) {
    46. this.describe = describe;
    47. }
    48. }

    1.2 构建定时任务的抽象类

    我们需要构建不同的定时类型,不同的定时类型需要的参数也是不同的,因此我们需要抽象出定时的公用参数,将不同的参数多态实现

    1. /**
    2. * 构建定时任务基础对象
    3. * @author jiangwei
    4. *
    5. */
    6. public class TaskInfo {
    7. /**
    8. * 该定时的任务处理器
    9. */
    10. private Classextends QuartzTaskJob> taskClass;
    11. /**
    12. * 任务名
    13. */
    14. private String taskName;
    15. /**
    16. * 任务组名
    17. * */
    18. private String groupName;
    19. /**
    20. * 任务描述
    21. * */
    22. private String description;
    23. /**
    24. * 任务类型
    25. */
    26. private TriggerType type;
    27. /**
    28. * 任务参数,可在具体的QuartzTaskJob实现中获取这些参数
    29. * */
    30. private Map param;
    31. /**
    32. * 任务状态
    33. * */
    34. private String taskStatus;
    35. public TaskInfo(Class taskClass, String taskName, String groupName, String description, TriggerType type, Map param) {
    36. this.taskClass = taskClass;
    37. this.taskName = taskName;
    38. this.groupName = groupName;
    39. this.description = description;
    40. this.type = type;
    41. this.param = param;
    42. }
    43. public TaskInfo(Class taskClass, String taskName, String groupName, String description, TriggerType type) {
    44. this.taskClass = taskClass;
    45. this.taskName = taskName;
    46. this.groupName = groupName;
    47. this.description = description;
    48. this.type = type;
    49. }
    50. public Classextends QuartzTaskJob> getTaskClass() {
    51. return taskClass;
    52. }
    53. public void setTaskClass(Class taskClass) {
    54. this.taskClass = taskClass;
    55. }
    56. public String getTaskName() {
    57. return taskName;
    58. }
    59. public void setTaskName(String taskName) {
    60. this.taskName = taskName;
    61. }
    62. public String getGroupName() {
    63. return groupName;
    64. }
    65. public void setGroupName(String groupName) {
    66. this.groupName = groupName;
    67. }
    68. public String getDescription() {
    69. return description;
    70. }
    71. public void setDescription(String description) {
    72. this.description = description;
    73. }
    74. public TriggerType getType() {
    75. return type;
    76. }
    77. public void setType(TriggerType type) {
    78. this.type = type;
    79. }
    80. public Map getParam() {
    81. return param;
    82. }
    83. public void setParam(Map param) {
    84. this.param = param;
    85. }
    86. public String getTaskStatus() {
    87. return taskStatus;
    88. }
    89. public void setTaskStatus(String taskStatus) {
    90. this.taskStatus = taskStatus;
    91. }
    92. }

    1.3 用以构建CRON定时任务

    1. /**
    2. * 用以构建CRON定时任务
    3. * cron触发器
    4. * @author jiangwei
    5. *
    6. */
    7. public class CronInfo extends TaskInfo{
    8. /**
    9. * cron表达式
    10. * */
    11. private String cronExpression;
    12. public CronInfo(Class taskClass, String taskName, String groupName, String description,
    13. Map param,String cronExpression) {
    14. super(taskClass, taskName, groupName, description, TriggerType.CRON, param);
    15. this.cronExpression = cronExpression;
    16. }
    17. public CronInfo(Class taskClass, String taskName, String groupName,
    18. String description,String cronExpression) {
    19. super(taskClass, taskName, groupName, description, TriggerType.CRON);
    20. this.cronExpression = cronExpression;
    21. }
    22. public String getCronExpression() {
    23. return cronExpression;
    24. }
    25. public void setCronExpression(String cronExpression) {
    26. this.cronExpression = cronExpression;
    27. }
    28. }

    1.4 用以构建固定间隔定时任务

    1. /**
    2. * 用以构建固定间隔定时任务
    3. * @author jiangwei
    4. *
    5. */
    6. public class IntervalInfo extends TaskInfo{
    7. /**
    8. * 事件间隔,根据TriggerType确定单位,除了数值为毫秒,该数值必须在-2^32~2^31 (-2147483648 ~ 2147483647)
    9. * */
    10. private Long interval;
    11. /**
    12. * 重复次数,会执行该数值+1次,为空无限重复
    13. * */
    14. private Integer repeatCount;
    15. public IntervalInfo(Class taskClass, String taskName, String groupName, String description,
    16. TriggerType type, Map param, Long interval,Integer repeatCount) {
    17. super(taskClass, taskName, groupName, description, type, param);
    18. if (type != TriggerType.INTERVAL_MILLISECOND){
    19. if (interval<(-2^32)||interval>(2^31)){
    20. throw new TaskException("interval超出范围,除了类型为INTERVAL_MILLISECOND的数据间隔定时的interval范围必须在-2^32~2^31 (-2147483648 ~ 2147483647)");
    21. }
    22. }
    23. this.interval = interval;
    24. this.repeatCount = repeatCount;
    25. }
    26. public IntervalInfo(Class taskClass, String taskName, String groupName, String description, TriggerType type, Long interval,Integer repeatCount) {
    27. super(taskClass, taskName, groupName, description, type);
    28. if (type != TriggerType.INTERVAL_MILLISECOND){
    29. if (interval<(-2^32)||interval>(2^31)){
    30. throw new TaskException("interval超出范围,除了类型为INTERVAL_MILLISECOND的数据间隔定时的interval范围必须在-2^32~2^31 (-2147483648 ~ 2147483647)");
    31. }
    32. }
    33. this.interval = interval;
    34. this.repeatCount = repeatCount;
    35. }
    36. public Long getInterval() {
    37. return interval;
    38. }
    39. public void setInterval(Long interval) {
    40. this.interval = interval;
    41. }
    42. public Integer getRepeatCount() {
    43. return repeatCount;
    44. }
    45. public void setRepeatCount(Integer repeatCount) {
    46. this.repeatCount = repeatCount;
    47. }
    48. }

    2.抽象任务类

    1. /**
    2. * 抽象任务类
    3. * @author jiangwei
    4. *
    5. */
    6. public interface QuartzTaskJob extends Job{
    7. /**
    8. * 执行定时任务
    9. */
    10. @Override
    11. void execute(JobExecutionContext context) throws JobExecutionException;
    12. }

    2.1 实现一个测试任务

    1. /**
    2. * 实现一个测试任务
    3. * @author jiangwei
    4. *
    5. */
    6. @Component
    7. public class TestQuartz implements QuartzTaskJob{
    8. private Logger log=LoggerFactory.getLogger(TestQuartz.class);
    9. @Override
    10. public void execute(JobExecutionContext context) throws JobExecutionException {
    11. // 获取参数
    12. JobDataMap jobDataMap = context.getTrigger().getJobDataMap();
    13. // 获取任务名
    14. String name = context.getJobDetail().getJobBuilder().build().getKey().getName();
    15. // 获取任务分组
    16. String group = context.getJobDetail().getJobBuilder().build().getKey().getGroup();
    17. // 获取任务描述
    18. String description = context.getJobDetail().getDescription();
    19. if (context.getTrigger() instanceof SimpleTrigger){
    20. // 运行次数
    21. System.out.println(((SimpleTrigger)context.getTrigger()).getTimesTriggered());
    22. }
    23. log.info("----------------------" +
    24. "\n任务组:{}\n任务名:{}\n任务描述:{}\n获取参数paramKey:{}\n" +
    25. "----------------------"
    26. ,name,group,description,jobDataMap.getString("paramKey"));
    27. try {
    28. // QuartzJobManager.getInstance().jobdelete(this.getClass().getSimpleName(),"ah");//执行完此任务就删除自己
    29. } catch (Exception e) {
    30. e.printStackTrace();
    31. }
    32. }
    33. }

    3. 构建触发器的不同实现

    3.1 抽象触发器实现

    1. /**
    2. * 触发器工厂
    3. *
    4. * @author jiangwei
    5. *
    6. */
    7. public interface ITriggerFactory {
    8. /**
    9. * 判断是否为该类型的触发器
    10. *
    11. * @param triggerType 触发器类型
    12. * @return boolean 如果是该类型的触发器返回true 否则返回false
    13. */
    14. public boolean check(TriggerType triggerType);
    15. /**
    16. * 创建触发器
    17. * @param taskInfo:定时任务基础信息
    18. * @return Trigger
    19. */
    20. public Trigger build(TaskInfo taskInfo);
    21. }

    3.2 CRON触发器

    1. /**
    2. *
    3. * CRON触发器
    4. *
    5. * @author jiangwei
    6. *
    7. */
    8. @Component
    9. public class CronTrigger implements ITriggerFactory{
    10. @Override
    11. public boolean check(TriggerType triggerType) {
    12. return triggerType==TriggerType.CRON;
    13. }
    14. @Override
    15. public Trigger build(TaskInfo taskInfo) {
    16. if (!(taskInfo instanceof CronInfo)){
    17. throw new TaskException("构建类型为CRON定时必须传入CronTimingModel.class的实现类");
    18. }
    19. //按新的cronExpression表达式构建一个新的trigger
    20. CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(((CronInfo) taskInfo).getCronExpression());
    21. return TriggerBuilder.newTrigger().withIdentity(taskInfo.getTaskName(), taskInfo.getTaskName())
    22. .withSchedule(scheduleBuilder).build();
    23. }
    24. }

    3.3 固定间隔触发器

    1. /**
    2. *
    3. * 固定间隔触发器
    4. *
    5. * @author jiangwei
    6. *
    7. */
    8. @Component
    9. public class IntervalTrigger implements ITriggerFactory{
    10. @Override
    11. public boolean check(TriggerType triggerType) {
    12. return triggerType == TriggerType.INTERVAL_MINUTE || triggerType == TriggerType.INTERVAL_SECOND || triggerType == TriggerType.INTERVAL_MILLISECOND||triggerType == TriggerType.INTERVAL_HOUR;
    13. }
    14. @Override
    15. public Trigger build(TaskInfo taskInfo) {
    16. if (!(taskInfo instanceof IntervalInfo)){
    17. throw new TaskException("构建类型为INTERVAL定时必须传入IntervalTimingMode.class的实现类");
    18. }
    19. //创建触发器
    20. SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
    21. Long interval = ((IntervalInfo) taskInfo).getInterval();
    22. Integer repeatCount = ((IntervalInfo) taskInfo).getRepeatCount();
    23. switch (taskInfo.getType()){
    24. case INTERVAL_MINUTE:
    25. simpleScheduleBuilder.withIntervalInMinutes(Math.toIntExact(interval));
    26. break;
    27. case INTERVAL_HOUR:
    28. simpleScheduleBuilder.withIntervalInHours(Math.toIntExact(interval));
    29. break;
    30. case INTERVAL_SECOND:
    31. simpleScheduleBuilder.withIntervalInSeconds(Math.toIntExact(interval));
    32. break;
    33. case INTERVAL_MILLISECOND:
    34. simpleScheduleBuilder.withIntervalInMilliseconds(interval);
    35. break;
    36. default:
    37. break;
    38. }
    39. if (repeatCount==null){
    40. // 无限重复
    41. simpleScheduleBuilder.repeatForever();
    42. }else {
    43. simpleScheduleBuilder.withRepeatCount(repeatCount);
    44. }
    45. return TriggerBuilder.newTrigger().withIdentity(taskInfo.getTaskName(), taskInfo.getTaskName())
    46. .withSchedule(simpleScheduleBuilder).build();
    47. }
    48. }

    3.4 构建触发器工厂

    1. /**
    2. *
    3. * 构建触发器工厂
    4. * 触发器管理器, 用来生成触发器
    5. *
    6. * @author jiangwei
    7. *
    8. */
    9. @Component
    10. public class TriggerManager {
    11. private final List triggerFactories;
    12. public TriggerManager(List triggerFactories) {
    13. this.triggerFactories = triggerFactories;
    14. }
    15. /**
    16. * 生成对应的触发器
    17. *
    18. * @param timingModel 触发器model
    19. * @return org.quartz.Trigger
    20. * @author YuanXiaohan
    21. * @date 2021/12/16 2:53 下午
    22. */
    23. public Trigger build(TaskInfo taskInfo) {
    24. for (ITriggerFactory triggerFactory : triggerFactories) {
    25. if (triggerFactory.check(taskInfo.getType())) {
    26. return triggerFactory.build(taskInfo);
    27. }
    28. }
    29. return null;
    30. }
    31. }

    3. 构建定时管理类

    1. /**
    2. * 构建定时管理类
    3. *
    4. * 添加定时
    5. * 更新定时触发器
    6. * 更新任务参数
    7. * 删除任务
    8. * 暂停任务
    9. * 将暂停的任务恢复执行
    10. * 启动所有任务
    11. * 关闭定时任务
    12. * 获取所有任务
    13. * @author jiangwei
    14. *
    15. */
    16. @Configuration
    17. public class QuartzTaskManager {
    18. private Logger log=LoggerFactory.getLogger(QuartzTaskManager.class);
    19. private final Scheduler scheduler;
    20. private final Boolean initStatus;
    21. private final TriggerManager triggerManager;
    22. private static QuartzTaskManager taskManager;
    23. public QuartzTaskManager(Scheduler scheduler, TriggerManager triggerManager) {
    24. this.scheduler = scheduler;
    25. taskManager = this;
    26. boolean status = true;
    27. try {
    28. // 启动调度器
    29. scheduler.start();
    30. } catch (SchedulerException e) {
    31. log.error("定时器调度器启动失败,定时器不可用!", e);
    32. status = false;
    33. }
    34. initStatus = status;
    35. this.triggerManager = triggerManager;
    36. }
    37. public static QuartzTaskManager getInstance(){
    38. return taskManager;
    39. }
    40. /**
    41. * 添加定时任务
    42. *
    43. * @param timingModel 任务model
    44. */
    45. public void addTask(TaskInfo taskInfo) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
    46. InstantiationException, SchedulerException {
    47. checkTimingInit();
    48. // 构建任务信息
    49. JobDetail jobDetail = JobBuilder.newJob(taskInfo.getTaskClass().getDeclaredConstructor().newInstance().getClass())
    50. .withDescription(taskInfo.getDescription())
    51. .withIdentity(taskInfo.getTaskName(), taskInfo.getGroupName())
    52. .build();
    53. // 构建触发器
    54. Trigger trigger = triggerManager.build(taskInfo);
    55. // 将任务参数放入触发器中
    56. if (taskInfo.getParam() != null && !taskInfo.getParam().isEmpty()) {
    57. trigger.getJobDataMap().putAll(taskInfo.getParam());
    58. }
    59. // 启动任务
    60. scheduler.scheduleJob(jobDetail, trigger);
    61. }
    62. /**
    63. * 更新任务,任务的标示(由taskName和groupName组成)不变,任务的触发器(触发频率)发生变化
    64. *
    65. * @param timingModel 任务model
    66. */
    67. public void updateTask(TaskInfo taskInfo) throws SchedulerException {
    68. // 获取到任务
    69. TriggerKey triggerKey = TriggerKey.triggerKey(taskInfo.getTaskName(), taskInfo.getGroupName());
    70. // 构建触发器
    71. Trigger trigger = triggerManager.build(taskInfo);
    72. // 将任务参数放入触发器中
    73. if (taskInfo.getParam() != null && !taskInfo.getParam().isEmpty()) {
    74. trigger.getJobDataMap().putAll(taskInfo.getParam());
    75. }
    76. // 将新的触发器绑定到任务标示上重新执行
    77. scheduler.rescheduleJob(triggerKey, trigger);
    78. }
    79. /**
    80. * 更新任务参数
    81. *
    82. * @param taskName 任务名
    83. * @param groupName 任务组名
    84. * @param param 参数
    85. */
    86. public void updateTask(String taskName, String groupName, Map param) throws SchedulerException {
    87. // 获取到任务
    88. TriggerKey triggerKey = TriggerKey.triggerKey(taskName, groupName);
    89. Trigger trigger = scheduler.getTrigger(triggerKey);
    90. //修改参数
    91. trigger.getJobDataMap().putAll(param);
    92. // 将新的触发器绑定到任务标示上重新执行
    93. scheduler.rescheduleJob(triggerKey, trigger);
    94. }
    95. /**
    96. * 删除任务
    97. *
    98. * @param taskName 任务名
    99. * @param groupName 任务组
    100. */
    101. public void deleteTask(String taskName, String groupName) throws SchedulerException {
    102. // 暂停任务对应的触发器
    103. scheduler.pauseTrigger(TriggerKey.triggerKey(taskName, groupName));
    104. // 删除任务对应的触发器
    105. scheduler.unscheduleJob(TriggerKey.triggerKey(taskName, groupName));
    106. // 删除任务
    107. scheduler.deleteJob(JobKey.jobKey(taskName, groupName));
    108. }
    109. /**
    110. * 暂停任务
    111. *
    112. * @param taskName 添加任务时timingMode中的taskName
    113. * @param groupName 添加任务时timingMode中的groupName
    114. */
    115. public void pauseTask(String taskName, String groupName) throws SchedulerException {
    116. scheduler.pauseJob(JobKey.jobKey(taskName, groupName));
    117. }
    118. /**
    119. * 将暂停的任务恢复执行
    120. *
    121. * @param taskName 添加任务时timingMode中的taskName
    122. * @param groupName 添加任务时timingMode中的groupName
    123. */
    124. public void resumeTask(String taskName, String groupName) throws SchedulerException {
    125. scheduler.resumeJob(JobKey.jobKey(taskName, groupName));
    126. }
    127. /**
    128. * 启动所有任务
    129. *
    130. */
    131. public void startAllTasks() {
    132. try {
    133. scheduler.start();
    134. } catch (Exception e) {
    135. throw new RuntimeException(e);
    136. }
    137. }
    138. /**
    139. * 关闭定时任务,回收所有的触发器资源
    140. *
    141. */
    142. public void shutdownAllTasks() {
    143. try {
    144. if (!scheduler.isShutdown()) {
    145. scheduler.shutdown();
    146. }
    147. } catch (Exception e) {
    148. throw new RuntimeException(e);
    149. }
    150. }
    151. /**
    152. * 获取所有的任务,暂时无法获取到任务执行类和任务描述
    153. *
    154. * @return java.util.List
    155. * @author YuanXiaohan
    156. * @date 2021/12/16 3:37 下午
    157. */
    158. public List getTaskList() throws SchedulerException {
    159. GroupMatcher matcher = GroupMatcher.anyJobGroup();
    160. Set jobKeys = scheduler.getJobKeys(matcher);
    161. List taskList = new ArrayList();
    162. for (JobKey jobKey : jobKeys) {
    163. Listextends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
    164. for (Trigger trigger : triggers) {
    165. TaskInfo taskInfo;
    166. if (trigger instanceof CronTrigger) {
    167. taskInfo = new CronInfo(null, jobKey.getName(), jobKey.getGroup(), null, ((CronTrigger) trigger).getCronExpression());
    168. taskInfo.setTaskStatus(scheduler.getTriggerState(trigger.getKey()).name());
    169. taskList.add(taskInfo);
    170. } else {
    171. log.warn("name:{},group:{}的定时任务类型未知,请拓展QuartzTaskManager.getTaskList的任务类型解析", jobKey.getName(), jobKey.getGroup());
    172. }
    173. }
    174. }
    175. return taskList;
    176. }
    177. /**
    178. * 校验定时调度器是否初始化完成
    179. */
    180. private void checkTimingInit() {
    181. if (!initStatus) {
    182. throw new TaskException("定时器未初始化,添加定时器失败!");
    183. }
    184. }
    185. }

    4. Quartz注入到SpringBoot

    1. /**
    2. * Quartz注入到SpringBoot
    3. * @author jiangwei
    4. *
    5. */
    6. @Component
    7. public class TaskJobFactory extends AdaptableJobFactory{
    8. private final AutowireCapableBeanFactory capableBeanFactory;
    9. public TaskJobFactory(AutowireCapableBeanFactory capableBeanFactory) {
    10. this.capableBeanFactory = capableBeanFactory;
    11. }
    12. @Override
    13. protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
    14. //调用父类的方法
    15. Object jobInstance = super.createJobInstance(bundle);
    16. //进行注入
    17. capableBeanFactory.autowireBean(jobInstance);
    18. return jobInstance;
    19. }
    20. }

    5. 执行

    1. public class JwbastaThymeleafAdminApplicaiton implements CommandLineRunner{
    2. private static Logger logger=LoggerFactory.getLogger(JwbastaThymeleafAdminApplicaiton.class);
    3. public static void main(String[] args) {
    4. SpringApplication.run(JwbastaThymeleafAdminApplicaiton.class, args);
    5. }
    6. }
    7. @Override
    8. public void run(String... args) throws Exception {
    9. //构建CRON定时
    10. //CronTimingModel cronTimingModel = new CronTimingModel(TestQuartz.class, "测试名", "测试组", "测试描述", "*/1 * * * * ?");
    11. // 构建固定间隔定时
    12. IntervalInfo intervalTimingMode = new IntervalInfo(TestQuartz.class, "测试名", "测试组", "测试描述", TriggerType.INTERVAL_SECOND, 5L,null);
    13. HashMap param = new HashMap<>();
    14. param.put("paramKey","获取到参数了");
    15. intervalTimingMode.setParam(param);
    16. QuartzTaskManager.getInstance().addTask(intervalTimingMode);
    17. }
    18. }

    jwbasta官网

  • 相关阅读:
    【pandas小技巧】--修改列的名称
    Lua中如何实现类似gdb的断点调试--05优化断点信息数据结构
    VUE之旅—day3
    王道数据结构课后代码题p150 15.设有一棵满二叉树(所有结点值均不同),已知其先序序列为 pre,设计一个算法求其后序序列post。(c语言代码实现)
    poj 2182 Lost Cows (树状数组 || 线段树)
    如何解决AI场景下的冯诺伊曼陷阱?
    DiagnosisPrintDialog 使用广播导致关闭不了的问题
    LeetCode 775. 全局倒置与局部倒置
    手机厂商“互卷”之年:“机海战术”失灵,“慢节奏”打法崛起
    R语言读取(加载)txt格式数据为dataframe、为dataframe中的两个离散变量构建列联表
  • 原文地址:https://blog.csdn.net/xiaowei20091124/article/details/126778764