0


SpringBoot创建动态定时任务的几种方式

一、使用 @Scheduled 注解

  1. @Scheduled Spring 提供的一个注解,用于标记需要定时执行的方法。常见的属性包括:
  • cron :通过 Cron 表达式定义任务的执行时间。
  • fixedRate :定义任务的固定执行频率,以毫秒为单位。
  • fixedDelay :定义任务在前一次执行完毕后延迟多少毫秒再执行。

代码示例:

  1. import org.springframework.scheduling.annotation.Scheduled;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class ScheduledTasks {
  5. @Scheduled(cron = "0 0 * * * ?") // 每小时整点执行一次
  6. public void reportCurrentTime() {
  7. System.out.println("现在时间:" + System.currentTimeMillis());
  8. }
  9. @Scheduled(fixedRate = 5000) // 每5秒执行一次
  10. public void fixedRateTask() {
  11. System.out.println("每5秒执行一次任务:" + System.currentTimeMillis());
  12. }
  13. @Scheduled(fixedDelay = 7000) // 前一次执行完毕后延迟7秒执行
  14. public void fixedDelayTask() {
  15. System.out.println("延迟7秒后执行任务:" + System.currentTimeMillis());
  16. }
  17. }
  1. @Scheduled 适用于大多数简单的定时任务场景,如定时发送邮件或生成报告等。然而,它的灵活性较差,对于复杂的任务调度需求,或需要动态调整任务时间的场景,可能并不适用。

二、使用 SchedulingConfigurer 接口

  1. SchedulingConfigurer 接口允许我们通过编程方式配置任务调度器(TaskScheduler)。通过实现这个接口,我们可以灵活地设置任务的调度规则,甚至动态地添加或移除任务。

简单使用代码:

  1. public class TaskConfig implements SchedulingConfigurer {
  2. @Override
  3. public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
  4. scheduledTaskRegistrar.addTriggerTask(
  5. //1.添加任务内容(Runnable)
  6. () -> System.out.println("执行定时任务2: " + LocalDateTime.now().toLocalTime()),
  7. //2.设置执行周期(Trigger)
  8. triggerContext -> {
  9. //2.1 从数据库获取执行周期
  10. String cron = zhyMapper.getCron();
  11. //2.2 合法性校验.
  12. if (StringUtils.isEmpty(cron)) {
  13. // Omitted Code ..
  14. }
  15. //2.3 返回执行周期(Date)
  16. return new CronTrigger(cron).nextExecutionTime(triggerContext);
  17. }
  18. );
  19. }
  20. }

详细增删该查操作:

  • 动态注册bean
  1. public class ApplicationContextUtils implements ApplicationContextAware {
  2. private static ApplicationContext CONTEXT;
  3. /**
  4. * 设置spring上下文
  5. * @param ctx spring上下文
  6. * @throws BeansException
  7. * */
  8. @Override
  9. public void setApplicationContext(ApplicationContext ctx) throws BeansException {
  10. CONTEXT = ctx;
  11. }
  12. /**
  13. * 获取容器
  14. * @return
  15. */
  16. public static ApplicationContext getApplicationContext() {
  17. return CONTEXT;
  18. }
  19. /**
  20. * 获取容器对象
  21. * @param type
  22. * @param <T>
  23. * @return
  24. */
  25. public static <T> T getBean(Class<T> type) {
  26. return CONTEXT.getBean(type);
  27. }
  28. public static <T> T getBean(String name,Class<T> clazz){
  29. return CONTEXT.getBean(name, clazz);
  30. }
  31. public static Object getBean(String name){
  32. return CONTEXT.getBean(name);
  33. }
  34. /**
  35. * springboot动态注册bean
  36. * @param clazz
  37. * @param <T>
  38. * @return
  39. */
  40. public static <T> T register(Class<T> clazz) {
  41. ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) ApplicationContextUtils.getApplicationContext();
  42. DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
  43. BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
  44. if(defaultListableBeanFactory.getBeanNamesForType(clazz).length > 0) {
  45. return defaultListableBeanFactory.getBean(clazz);
  46. }
  47. defaultListableBeanFactory.registerBeanDefinition(clazz.getName(), beanDefinitionBuilder.getRawBeanDefinition());
  48. return (T) ApplicationContextUtils.getBean(clazz.getName());
  49. }
  50. }
  • 任务实体
  1. public class Job {
  2. /**
  3. * 任务id, 用于标识,默认使用全限定类名
  4. */
  5. private String jobId;
  6. /**
  7. * 任务名称, 默认简单类名
  8. */
  9. private String jobName;
  10. /**
  11. * cron表达式, 修改后即可生效
  12. */
  13. private String cron;
  14. /**
  15. * 任务描述
  16. */
  17. private String description;
  18. /**
  19. * 是否启用, 默认启用, 修改后即可生效
  20. */
  21. private boolean enable = true;
  22. /**
  23. * 是否处于等待执行下个任务的状态
  24. */
  25. private boolean active;
  26. /**
  27. * 任务运行类
  28. */
  29. private Class<? extends Runnable> clazz;
  30. }
  • 操作类
  1. public class JobHandler {
  2. private ScheduledTask scheduledTask;
  3. private TriggerTask triggerTask;
  4. private TriggerContext triggerContext;
  5. }
  • 配置SchedulingConfigurer
  1. public class JobSchedulingConfigurer implements SchedulingConfigurer {
  2. private ScheduledTaskRegistrar registrar;
  3. /**
  4. * 线程池任务调度器
  5. */
  6. @Bean
  7. public TaskScheduler taskScheduler() {
  8. ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
  9. scheduler.setPoolSize(Runtime.getRuntime().availableProcessors() / 3 + 1);
  10. scheduler.setThreadNamePrefix("TaskScheduler-");
  11. scheduler.setRemoveOnCancelPolicy(true); // 保证能立刻丢弃运行中的任务
  12. taskScheduler = scheduler; // 获取 句柄,方便后期获取 future
  13. return scheduler;
  14. }
  15. @Override
  16. public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
  17. scheduledTaskRegistrar.setTaskScheduler(taskScheduler());
  18. this.registrar = scheduledTaskRegistrar;
  19. }
  20. public ScheduledTaskRegistrar getRegistrar() {
  21. return registrar;
  22. }
  23. public void setRegistrar(ScheduledTaskRegistrar registrar) {
  24. this.registrar = registrar;
  25. }
  26. }
  • 增删改查
  1. public class SchedulerManager {
  2. /**
  3. * 任务容器
  4. */
  5. private Map<Job, JobHandler> tasks = new ConcurrentHashMap<>();
  6. /**
  7. * 任务注册
  8. */
  9. @Autowired
  10. private JobSchedulingConfigurer register;
  11. /**
  12. * 新增任务, 自生效
  13. * @param job 任务实体
  14. * @return 返回新增的任务
  15. */
  16. public Job addJob(Job job) {
  17. Assert.notNull(job, "job can't be null");
  18. ScheduledTaskRegistrar registrar = register.getRegistrar();
  19. Runnable runnable = ApplicationContextUtils.register(job.getClazz());
  20. if(job.getJobId() == null || "".equals(job.getJobId())) {
  21. job.setJobId(job.getClazz().getName());
  22. }
  23. Assert.isNull(this.getJob(job.getJobId()), "任务[" + job.getJobId() + "]已存在");
  24. if(job.getJobName() == null || "".equals(job.getJobName())) {
  25. job.setJobName(ClassUtils.getShortName(job.getClazz()));
  26. }
  27. CronExpress cron = AnnotationUtils.findAnnotation(job.getClazz(), CronExpress.class);
  28. if(cron != null && !"".equals(cron.value())) {
  29. // 注解的属性,大于配置的属性,方便调试
  30. job.setCron(cron.value());
  31. }
  32. job.setEnable(true);
  33. job.setActive(true);
  34. JobHandler entity = new JobHandler();
  35. TriggerTask triggerTask = new TriggerTask(runnable, (TriggerContext triggerContext) -> {
  36. // 每次任务执行均会进入此方法
  37. CronTrigger trigger = new CronTrigger(job.getCron());
  38. entity.setTriggerContext(triggerContext);
  39. return job.isEnable() ? trigger.nextExecutionTime(triggerContext) : null;
  40. });
  41. ScheduledTask scheduledTask = registrar.scheduleTriggerTask(triggerTask);
  42. entity.setScheduledTask(scheduledTask);
  43. entity.setTriggerTask(triggerTask);
  44. tasks.put(job, entity);
  45. return job;
  46. }
  47. /**
  48. * 任务类(必须标注了@CronExpress注解,且实现了Runnable接口)
  49. * @param clazz 接口类
  50. * @return 任务对象
  51. */
  52. public Job addJob(Class<? extends Runnable> clazz) {
  53. Job job = new Job();
  54. job.setClazz(clazz);
  55. return this.addJob(job);
  56. }
  57. /**
  58. * 获取任务操作对象
  59. * @param jobId 任务id
  60. * @return 任务操作对象
  61. */
  62. public JobHandler getJobHandler(String jobId) {
  63. return tasks.get(new Job(jobId));
  64. }
  65. /**
  66. * 根据任务id获取任务
  67. * @param jobId 任务id
  68. * @return 任务实体
  69. */
  70. public Job getJob(String jobId) {
  71. Assert.hasText(jobId, "jobId can't be null");
  72. Set<Job> jobs = tasks.keySet();
  73. if(jobs.size() == 0) {
  74. return null;
  75. }
  76. Iterator<Job> iterator = jobs.iterator();
  77. while (iterator.hasNext()) {
  78. Job next = iterator.next();
  79. if(jobId.equals(next.getJobId())) {
  80. return next;
  81. }
  82. }
  83. return null;
  84. }
  85. /**
  86. * 关闭任务(若任务正在执行,待任务执行完)
  87. * @param jobId 任务id
  88. * @return 是否关闭成功
  89. */
  90. public boolean shutDown(String jobId) {
  91. try {
  92. JobHandler handler = this.getJobHandler(jobId);
  93. Assert.notNull(handler, "任务[" + jobId + "]不存在");
  94. handler.getScheduledTask().cancel();
  95. Job job = getJob(jobId);
  96. job.setActive(false);
  97. return true;
  98. } catch (Exception e) {
  99. return false;
  100. }
  101. }
  102. /**
  103. * 启动已经注册的任务
  104. * @param jobId 任务id
  105. * @return 是否成功启动
  106. */
  107. public boolean startUp(String jobId) {
  108. try {
  109. JobHandler handler = this.getJobHandler(jobId);
  110. Assert.notNull(handler, "任务[" + jobId + "]不存在");
  111. register.getRegistrar().scheduleTriggerTask(handler.getTriggerTask());
  112. Job job = getJob(jobId);
  113. job.setActive(true);
  114. return true;
  115. } catch (Exception e) {
  116. return false;
  117. }
  118. }
  119. /**
  120. * 获取所有的任务实体
  121. * @return
  122. */
  123. public List<Job> getJobs() {
  124. return new ArrayList<>(tasks.keySet());
  125. }
  126. /**
  127. * 删除任务,先关闭再删除
  128. * @param jobId
  129. * @return
  130. */
  131. public boolean deleteJob(String jobId) {
  132. try {
  133. Job job = this.getJob(jobId);
  134. Assert.notNull(job, "任务[" + jobId + "]不存在");
  135. shutDown(jobId);
  136. tasks.remove(job);
  137. return true;
  138. } catch (Exception e) {
  139. return false;
  140. }
  141. }
  142. }
  • 任务运行类
  1. @CronExpress("0/1 * * * * *")
  2. public class HelloJob implements Runnable {
  3. @Override
  4. public void run() {
  5. System.out.println("hello msg" + Thread.currentThread().getId());
  6. }
  7. }
  • 测试
  1. ConfigurableApplicationContext ctx = SpringApplication.run(UnifiedTaskScheduleStarterApplication.class, args);
  2. SchedulerManager schedulerManager = ctx.getBean(SchedulerManager.class);
  3. Job job = schedulerManager.addJob(com.github.softwarevax.web.HelloJob.class);
  4. schedulerManager.shutDown(job.getJobId());

三、使用 TaskScheduler

  1. TaskScheduler Spring提供的用于调度任务的核心接口。通过 TaskScheduler,你可以灵活地安排任务的执行时间,并且可以在运行时动态地创建、取消任务。

代码示例简单使用:

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.scheduling.TaskScheduler;
  4. import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
  5. @Configuration
  6. public class TaskSchedulerConfig {
  7. @Bean
  8. public TaskScheduler taskScheduler() {
  9. ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
  10. scheduler.setPoolSize(5);
  11. scheduler.setThreadNamePrefix("MyScheduler-");
  12. return scheduler;
  13. }
  14. }

在使用 TaskScheduler 时,可以通过 schedule 方法动态安排任务:

  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.scheduling.TaskScheduler;
  3. import org.springframework.stereotype.Component;
  4. import java.util.Date;
  5. @Component
  6. public class DynamicTask {
  7. @Autowired
  8. private TaskScheduler taskScheduler;
  9. public void scheduleTask() {
  10. taskScheduler.schedule(() -> System.out.println("动态任务执行:" + System.currentTimeMillis()), new Date(System.currentTimeMillis() + 5000));
  11. }
  12. }

spring boot使用TaskScheduler实现动态增删启停定时任务

  • SchedulingConfig:添加执行定时任务的线程池配置类
  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.scheduling.TaskScheduler;
  4. import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
  5. @Configuration
  6. public class SchedulingConfig {
  7. @Bean
  8. public TaskScheduler taskScheduler() {
  9. ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
  10. // 定时任务执行线程池核心线程数
  11. taskScheduler.setPoolSize(4);
  12. taskScheduler.setRemoveOnCancelPolicy(true);
  13. taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");
  14. return taskScheduler;
  15. }
  16. }
  • ScheduledTask:添加ScheduledFuture的包装类

ScheduledFuture是ScheduledExecutorService定时任务线程池的执行结果。

  1. import java.util.concurrent.ScheduledFuture;
  2. public final class ScheduledTask {
  3. volatile ScheduledFuture<?> future;
  4. /**
  5. * 取消定时任务
  6. */
  7. public void cancel() {
  8. ScheduledFuture<?> future = this.future;
  9. if (future != null) {
  10. future.cancel(true);
  11. }
  12. }
  13. }
  • SchedulingRunnable:添加Runnable接口实现类

添加Runnable接口实现类,被定时任务线程池调用,用来执行指定bean里面的方法

  1. import org.apache.commons.lang.StringUtils;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import org.springframework.util.ReflectionUtils;
  5. import java.lang.reflect.Method;
  6. import java.util.Objects;
  7. public class SchedulingRunnable implements Runnable {
  8. private static final Logger logger = LoggerFactory.getLogger(SchedulingRunnable.class);
  9. private final String beanName;
  10. private final String methodName;
  11. private final String params;
  12. public SchedulingRunnable(String beanName, String methodName) {
  13. this(beanName, methodName, null);
  14. }
  15. public SchedulingRunnable(String beanName, String methodName, String params) {
  16. this.beanName = beanName;
  17. this.methodName = methodName;
  18. this.params = params;
  19. }
  20. @Override
  21. public void run() {
  22. logger.info("定时任务开始执行 - bean:{},方法:{},参数:{}", beanName, methodName, params);
  23. long startTime = System.currentTimeMillis();
  24. try {
  25. Object target = SpringContextUtils.getBean(beanName);
  26. Method method = null;
  27. if (StringUtils.isNotEmpty(params)) {
  28. method = target.getClass().getDeclaredMethod(methodName, String.class);
  29. } else {
  30. method = target.getClass().getDeclaredMethod(methodName);
  31. }
  32. ReflectionUtils.makeAccessible(method);
  33. if (StringUtils.isNotEmpty(params)) {
  34. method.invoke(target, params);
  35. } else {
  36. method.invoke(target);
  37. }
  38. } catch (Exception ex) {
  39. logger.error(String.format("定时任务执行异常 - bean:%s,方法:%s,参数:%s ", beanName, methodName, params), ex);
  40. }
  41. long times = System.currentTimeMillis() - startTime;
  42. logger.info("定时任务执行结束 - bean:{},方法:{},参数:{},耗时:{} 毫秒", beanName, methodName, params, times);
  43. }
  44. @Override
  45. public boolean equals(Object o) {
  46. if (this == o) return true;
  47. if (o == null || getClass() != o.getClass()) return false;
  48. SchedulingRunnable that = (SchedulingRunnable) o;
  49. if (params == null) {
  50. return beanName.equals(that.beanName) &&
  51. methodName.equals(that.methodName) &&
  52. that.params == null;
  53. }
  54. return beanName.equals(that.beanName) &&
  55. methodName.equals(that.methodName) &&
  56. params.equals(that.params);
  57. }
  58. @Override
  59. public int hashCode() {
  60. if (params == null) {
  61. return Objects.hash(beanName, methodName);
  62. }
  63. return Objects.hash(beanName, methodName, params);
  64. }
  65. }
  • CronTaskRegistrar:添加定时任务注册类,用来增加、删除定时任务
  1. import com.example.testspringboot.cron.ScheduleResult;
  2. import org.springframework.beans.factory.DisposableBean;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.scheduling.TaskScheduler;
  5. import org.springframework.scheduling.config.CronTask;
  6. import org.springframework.stereotype.Component;
  7. import java.util.*;
  8. import java.util.concurrent.ConcurrentHashMap;
  9. /**
  10. * 添加定时任务注册类,用来增加、删除定时任务。
  11. */
  12. @Component
  13. public class CronTaskRegistrar implements DisposableBean {
  14. private final Map<Runnable, ScheduledTask> scheduledTasks = new ConcurrentHashMap<>(16);
  15. private final Map<Integer, ScheduleResult> schedulerJob = new HashMap<>();
  16. @Autowired
  17. private TaskScheduler taskScheduler;
  18. public TaskScheduler getScheduler() {
  19. return this.taskScheduler;
  20. }
  21. public void addCronTask(ScheduleResult scheduleResult) {
  22. SchedulingRunnable task = new SchedulingRunnable(scheduleResult.getBeanName(), scheduleResult.getMethodName(), scheduleResult.getMethodParams());
  23. String cronExpression = scheduleResult.getCronExpression();
  24. CronTask cronTask = new CronTask(task, cronExpression);
  25. // 如果当前包含这个任务,则移除
  26. if (this.scheduledTasks.containsKey(task)) {
  27. removeCronTask(scheduleResult.getBeanName(), scheduleResult.getMethodName(), scheduleResult.getMethodParams());
  28. }
  29. schedulerJob.put(scheduleResult.getJobId(), scheduleResult);
  30. this.scheduledTasks.put(task, scheduleCronTask(cronTask));
  31. }
  32. public void removeCronTask(String beanName, String methodName, String methodParams) {
  33. SchedulingRunnable task = new SchedulingRunnable(beanName, methodName, methodParams);
  34. ScheduledTask scheduledTask = this.scheduledTasks.remove(task);
  35. if (scheduledTask != null) {
  36. scheduledTask.cancel();
  37. }
  38. }
  39. public void removeCronTask(ScheduleResult scheduleResult) {
  40. schedulerJob.put(scheduleResult.getJobId(), scheduleResult);
  41. removeCronTask(scheduleResult.getBeanName(), scheduleResult.getMethodName(), scheduleResult.getMethodParams());
  42. }
  43. public ScheduledTask scheduleCronTask(CronTask cronTask) {
  44. ScheduledTask scheduledTask = new ScheduledTask();
  45. scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());
  46. return scheduledTask;
  47. }
  48. public Map<Runnable, ScheduledTask> getScheduledTasks() {
  49. return scheduledTasks;
  50. }
  51. public Map<Integer, ScheduleResult> getSchedulerJob() {
  52. return schedulerJob;
  53. }
  54. @Override
  55. public void destroy() {
  56. for (ScheduledTask task : this.scheduledTasks.values()) {
  57. task.cancel();
  58. }
  59. this.scheduledTasks.clear();
  60. }
  61. public ScheduleResult getSchedulerByJobId(Integer jobId) {
  62. for (ScheduleResult job : findAllTask()) {
  63. if (jobId.equals(job.getJobId())) {
  64. return job;
  65. }
  66. }
  67. return null;
  68. }
  69. public List<ScheduleResult> findAllTask() {
  70. List<ScheduleResult> ScheduleResults = new ArrayList<>();
  71. Set<Map.Entry<Integer, ScheduleResult>> entries = schedulerJob.entrySet();
  72. for (Map.Entry<Integer, ScheduleResult> en : entries) {
  73. ScheduleResults.add(en.getValue());
  74. }
  75. return ScheduleResults;
  76. }
  77. }
  • CronUtils:校验Cron表达式的有效性
  1. import org.springframework.scheduling.support.CronExpression;
  2. public class CronUtils {
  3. /**
  4. * 返回一个布尔值代表一个给定的Cron表达式的有效性
  5. *
  6. * @param cronExpression Cron表达式
  7. * @return boolean 表达式是否有效
  8. */
  9. public static boolean isValid(String cronExpression) {
  10. return CronExpression.isValidExpression(cronExpression);
  11. }
  12. }
  • ScheduleResult:添加定时任务实体类
  1. import lombok.Data;
  2. @Data
  3. public class ScheduleResult {
  4. /**
  5. * 任务ID
  6. */
  7. private Integer jobId;
  8. /**
  9. * bean名称
  10. */
  11. private String beanName;
  12. /**
  13. * 方法名称
  14. */
  15. private String methodName;
  16. /**
  17. * 方法参数: 执行service里面的哪一种方法
  18. */
  19. private String methodParams;
  20. /**
  21. * cron表达式
  22. */
  23. private String cronExpression;
  24. /**
  25. * 状态(1正常 0暂停)
  26. */
  27. private Integer jobStatus;
  28. /**
  29. * 备注
  30. */
  31. private String remark;
  32. /**
  33. * 创建时间
  34. */
  35. private String createTime;
  36. /**
  37. * 更新时间
  38. */
  39. private String updateTime;
  40. }
  • ScheduleJobStatus:任务状态枚举类型
  1. public enum ScheduleJobStatus {
  2. /**
  3. * 暂停
  4. */
  5. PAUSE,
  6. /**
  7. * 正常
  8. */
  9. NORMAL;
  10. }
  • SpringContextUtils类:从spring容器里获取bean
  1. import org.springframework.beans.BeansException;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.ApplicationContextAware;
  4. import org.springframework.stereotype.Component;
  5. @Component
  6. public class SpringContextUtils implements ApplicationContextAware {
  7. private static ApplicationContext applicationContext = null;
  8. @Override
  9. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  10. if (SpringContextUtils.applicationContext == null) {
  11. SpringContextUtils.applicationContext = applicationContext;
  12. }
  13. }
  14. public static ApplicationContext getApplicationContext() {
  15. return applicationContext;
  16. }
  17. // 通过name获取 Bean.
  18. public static Object getBean(String name) {
  19. return getApplicationContext().getBean(name);
  20. }
  21. // 通过class获取Bean.
  22. public static <T> T getBean(Class<T> clazz) {
  23. return getApplicationContext().getBean(clazz);
  24. }
  25. // 通过name,以及Clazz返回指定的Bean
  26. public static <T> T getBean(String name, Class<T> clazz) {
  27. return getApplicationContext().getBean(name, clazz);
  28. }
  29. public static boolean containsBean(String name) {
  30. return getApplicationContext().containsBean(name);
  31. }
  32. public static boolean isSingleton(String name) {
  33. return getApplicationContext().isSingleton(name);
  34. }
  35. public static Class<? extends Object> getType(String name) {
  36. return getApplicationContext().getType(name);
  37. }
  38. }
  • ScheduleJobService:增删启停service方法
  1. @Service
  2. @Slf4j
  3. public class ScheduleJobService {
  4. @Autowired
  5. private CronTaskRegistrar cronTaskRegistrar;
  6. public void addScheduleJob(ScheduleResult scheduleResult) {
  7. long currentTimeMillis = System.currentTimeMillis();
  8. scheduleResult.setCreateTime(formatTimeYMD_HMS_SSS(currentTimeMillis));
  9. scheduleResult.setUpdateTime(formatTimeYMD_HMS_SSS(currentTimeMillis));
  10. scheduleResult.setJobId(findAllTask().size() + 1);
  11. if (scheduleResult.getJobStatus().equals(ScheduleJobStatus.NORMAL.ordinal())) {
  12. log.info("Stop or pause: is now on");
  13. cronTaskRegistrar.addCronTask(scheduleResult);
  14. return;
  15. }
  16. cronTaskRegistrar.getSchedulerJob().put(scheduleResult.getJobId(), scheduleResult);
  17. }
  18. public void editScheduleJob(ScheduleResult currentSchedule) {
  19. //先移除
  20. cronTaskRegistrar.removeCronTask(currentSchedule.getBeanName(), currentSchedule.getMethodName(), currentSchedule.getMethodParams());
  21. ScheduleResult pastScheduleJob = cronTaskRegistrar.getSchedulerByJobId(currentSchedule.getJobId());
  22. if (pastScheduleJob == null) {
  23. System.out.println("没有这个任务");
  24. return;
  25. }
  26. //然后判断是否开启, 如果开启的话,现在立即执行
  27. startOrStopSchedulerJob(currentSchedule, true);
  28. }
  29. public void deleteScheduleJob(ScheduleResult scheduleResult) {
  30. // 清除这个任务
  31. cronTaskRegistrar.removeCronTask(scheduleResult.getBeanName(), scheduleResult.getMethodName(), scheduleResult.getMethodParams());
  32. // 清除这个任务的数据
  33. cronTaskRegistrar.getSchedulerJob().remove(scheduleResult.getJobId());
  34. }
  35. public void startOrStopScheduler(ScheduleResult scheduleResult) {
  36. cronTaskRegistrar.getSchedulerJob().get(scheduleResult.getJobId()).setJobStatus(scheduleResult.getJobStatus());
  37. startOrStopSchedulerJob(scheduleResult, false);
  38. }
  39. private void startOrStopSchedulerJob(ScheduleResult scheduleResult, boolean update) {
  40. // 更新时间
  41. scheduleResult.setUpdateTime(formatTimeYMD_HMS_SSS(System.currentTimeMillis()));
  42. if (scheduleResult.getJobStatus().equals(ScheduleJobStatus.NORMAL.ordinal())) {
  43. System.out.println("停止或暂停:现在是开启");
  44. cronTaskRegistrar.addCronTask(scheduleResult);
  45. return;
  46. }
  47. System.out.println("停止或暂停:现在是暂停");
  48. if (update){
  49. cronTaskRegistrar.removeCronTask(scheduleResult);
  50. return;
  51. }
  52. cronTaskRegistrar.removeCronTask(scheduleResult.getBeanName(), scheduleResult.getMethodName(), scheduleResult.getMethodParams());
  53. }
  54. public List<ScheduleResult> findAllTask() {
  55. return cronTaskRegistrar.findAllTask();
  56. }
  57. // 转换为年-月-日 时:分:秒
  58. private String formatTimeYMD_HMS_SSS(long time) {
  59. return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(time);
  60. }
  61. }
  • cronController:访问接口
  1. import com.example.testspringboot.cron.ScheduleResult;
  2. import com.example.testspringboot.cron.ScheduleJobService;
  3. import com.example.testspringboot.cron.utils.CronUtils;
  4. import com.google.gson.Gson;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.web.bind.annotation.*;
  7. import java.util.List;
  8. @RestController
  9. public class cronController {
  10. @Autowired
  11. private ScheduleJobService scheduleJobService;
  12. /**
  13. * 测试上传的用例文件, 获取详细执行结果
  14. */
  15. @PostMapping("/add")
  16. void executeTestOneFile(@RequestBody ScheduleResult scheduleResult) {
  17. boolean valid = CronUtils.isValid(scheduleResult.getCronExpression());
  18. if (valid){
  19. System.out.println("校验成功, 添加任务");
  20. scheduleResult.setMethodParams(scheduleResult.getBranch()+scheduleResult.getCaseDir());
  21. scheduleJobService.addScheduleJob(scheduleResult);
  22. }else {
  23. System.out.println("校验失败");
  24. }
  25. }
  26. @PostMapping("/stop")
  27. void end(@RequestBody ScheduleResult scheduleResult) {
  28. Gson gson = new Gson();
  29. System.out.println("================");
  30. System.out.println(scheduleResult);
  31. System.out.println("=================");
  32. scheduleResult.setJobStatus(0);
  33. scheduleJobService.startOrStopScheduler(scheduleResult);
  34. }
  35. @PostMapping("/start")
  36. void start(@RequestBody ScheduleResult scheduleResult) {
  37. System.out.println("================");
  38. System.out.println(scheduleResult);
  39. System.out.println("=================");
  40. scheduleResult.setJobStatus(1);
  41. scheduleJobService.startOrStopScheduler(scheduleResult);
  42. }
  43. @PostMapping("/edit")
  44. void edit(@RequestBody ScheduleResult scheduleResult) {
  45. System.out.println("=======edit=========");
  46. System.out.println(scheduleResult);
  47. System.out.println("=================");
  48. scheduleJobService.editScheduleJob(scheduleResult);
  49. }
  50. @PostMapping("/delete")
  51. void delete(@RequestBody ScheduleResult scheduleResult) {
  52. System.out.println("=======delete=========");
  53. System.out.println(scheduleResult);
  54. System.out.println("=================");
  55. scheduleJobService.deleteScheduleJob(scheduleResult);
  56. }
  57. @GetMapping("/tasks")
  58. List<ScheduleResult> get() throws Exception {
  59. List<ScheduleResult> allTask = scheduleJobService.findAllTask();
  60. System.out.println("现在的定时任务数量 = " + allTask.size());
  61. System.out.println("现在的定时任务 = " + allTask);
  62. return allTask;
  63. }
  64. }
  • 测试bean
  1. import org.springframework.stereotype.Component;
  2. @Component
  3. public class c1 {
  4. public void test1(String y){
  5. System.out.println("这个是test1的bean : " + y);
  6. }
  7. public void test2(){
  8. System.out.println("这个是test1的bean中test2方法");
  9. }
  10. }
  • 项目启动后的定时任务
  1. import com.example.testspringboot.cron.ScheduleJobService;
  2. import com.example.testspringboot.cron.ScheduleResult;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.boot.CommandLineRunner;
  5. import org.springframework.stereotype.Component;
  6. @Component
  7. public class init implements CommandLineRunner {
  8. @Autowired
  9. private ScheduleJobService scheduleJobService;
  10. @Override
  11. public void run(String... args) throws Exception {
  12. System.out.println("开始珍惜");
  13. ScheduleResult scheduleResult = new ScheduleResult();
  14. scheduleResult.setBeanName("c1");
  15. scheduleResult.setMethodName("test1");
  16. scheduleResult.setCronExpression("0/25 * * * * *");
  17. scheduleResult.setJobStatus(1);
  18. scheduleResult.setMethodParams("test1");
  19. scheduleJobService.addScheduleJob(scheduleResult);
  20. scheduleJobService.findAllTask();
  21. }
  22. }

四、使用 Quartz 实现定时任务

  1. Quartz 是一个功能强大的开源任务调度框架,支持复杂的任务调度需求,如任务的持久化、分布式任务管理、基于数据库的调度等。Spring Boot 提供了对 Quartz 的良好集成,使得在应用中使用 Quartz 变得更加简单。

简单使用:

  1. import org.quartz.*;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.scheduling.quartz.JobDetailFactoryBean;
  5. import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
  6. @Configuration
  7. public class QuartzConfig {
  8. @Bean
  9. public JobDetailFactoryBean jobDetail() {
  10. JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
  11. factoryBean.setJobClass(SampleJob.class);
  12. factoryBean.setDescription("Sample Quartz Job");
  13. factoryBean.setDurability(true);
  14. return factoryBean;
  15. }
  16. @Bean
  17. public SimpleTriggerFactoryBean trigger(JobDetail jobDetail) {
  18. SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
  19. factoryBean.setJobDetail(jobDetail);
  20. factoryBean.setRepeatInterval(5000); // 每5秒执行一次
  21. factoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
  22. return factoryBean;
  23. }
  24. public static class SampleJob implements Job {
  25. @Override
  26. public void execute(JobExecutionContext context) {
  27. System.out.println("Quartz任务执行:" + System.currentTimeMillis());
  28. }
  29. }
  30. }

springboot整合

  • 数据库设计

    1. 将任务计划放入数据库中保存。在启动任务是,从数据库中查找任务计划信息,并动态配置进去即可。
  1. DROP TABLE IF EXISTS `cc_task_info`;
  2. CREATE TABLE `cc_task_info` (
  3. `TID` int(11) NOT NULL AUTO_INCREMENT,
  4. `TASK_ANME` varchar(50) NOT NULL,
  5. `TASK_CODE` varchar(50) NOT NULL,
  6. `JOB_CLASS` varchar(200) NOT NULL,
  7. `JOB_GROUP` varchar(50) NOT NULL,
  8. `CRON` varchar(50) NOT NULL,
  9. `DEL_STATUS` varchar(2) DEFAULT '1' NULL,
  10. `CRT_TIME` datetime DEFAULT NULL,
  11. PRIMARY KEY (`TID`)
  12. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='定时任务管理表';
  13. DROP TABLE IF EXISTS `cc_task_record`;
  14. CREATE TABLE `cc_task_record` (
  15. `RID` int(11) NOT NULL AUTO_INCREMENT,
  16. `TASK_CODE` varchar(50) NOT NULL,
  17. `RUN_TIME` datetime NOT NULL,
  18. `RUN_CODE` char(1) NOT NULL,
  19. `RUN_MSG` varchar(100) NULL,
  20. PRIMARY KEY (`RID`)
  21. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='定时任务运行记录表';
  22. DROP TABLE IF EXISTS `cc_task_status`;
  23. CREATE TABLE `cc_task_status` (
  24. `TASK_CODE` varchar(50) NOT NULL,
  25. `TASK_STATUS` varchar(10) NOT NULL,
  26. `LST_SUCC_TIME` datetime NOT NULL,
  27. `LST_TIME` datetime NOT NULL,
  28. PRIMARY KEY (`TASK_CODE`)
  29. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='定时任务运行状态表';
  • 定时任务管理

    1. 通过Scheduler的方法来实现。Scheduler提供了一系列方法来管理定时任务的执行状态。

主要包括:

  • scheduleJob():添加定时任务
  • rescheduleJob():修改定时任务
  • pauseJob():暂停定时任务执行
  • resumeJob():恢复定时任务执行
  • deleteJob():删除定时任务执行

针对上述方法,只需要传入对应参数即可。建了一个QuartzService来管理定时任务,供业务层调用。

详细代码如下:

  1. /**
  2. * 定时任务管理服务
  3. */
  4. @Service
  5. public class QuartzService {
  6. public static String SCHEDULER_OPR_START = "start";
  7. public static String SCHEDULER_OPR_PAUSE = "pause";
  8. public static String SCHEDULER_OPR_RESUME = "resume";
  9. public static String SCHEDULER_OPR_REMOVE = "remove";
  10. @Autowired
  11. private Scheduler scheduler;
  12. /**
  13. * 启动任务
  14. */
  15. public void startJob(String taskCode, String taskAnme, String cron, String jobGroup,
  16. String className) throws Exception{
  17. Class<Job> jobClass = null;
  18. try {
  19. jobClass = (Class<Job>) Class.forName(className);//获取任务执行类
  20. } catch (ClassNotFoundException e) {
  21. throw new Exception("任务类不存在");
  22. }
  23. //创建job,指定job名称和分组
  24. JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(taskCode, jobGroup).build();
  25. //创建表达式工作计划
  26. CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
  27. //创建触发器
  28. CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(taskCode, jobGroup)
  29. .withSchedule(cronScheduleBuilder).build();
  30. scheduler.scheduleJob(jobDetail, cronTrigger);
  31. }
  32. /**
  33. * 修改定时任务执行时间
  34. * @param taskCode
  35. * @param jobGroup
  36. * @param cron 新的时间
  37. * @throws Exception
  38. */
  39. public void modifyJob(String taskCode, String jobGroup, String cron) throws Exception{
  40. TriggerKey triggerKey = new TriggerKey(taskCode, jobGroup);
  41. CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
  42. String oldCron = trigger.getCronExpression();
  43. if(!oldCron.equals(cron)){
  44. CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(taskCode, jobGroup)
  45. .withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
  46. Date date = scheduler.rescheduleJob(triggerKey, cronTrigger);
  47. if(date == null){
  48. throw new Exception("修改定时任务执行时间报错");
  49. }
  50. }
  51. }
  52. /**
  53. * 暂停某个定时任务(任务恢复后,暂停时间段内未执行的任务会继续执行,如暂停时间段内有2次,则会执行2次)
  54. * @param taskCode
  55. * @param jobGroup
  56. * @throws Exception
  57. */
  58. public void pauseJob(String taskCode, String jobGroup) throws Exception{
  59. JobKey jobKey = new JobKey(taskCode, jobGroup);
  60. JobDetail jobDetail = scheduler.getJobDetail(jobKey);
  61. if(jobDetail == null){
  62. return;
  63. }
  64. scheduler.pauseJob(jobKey);
  65. }
  66. /**
  67. * 恢复某个定时任务
  68. * @param taskCode
  69. * @param jobGroup
  70. * @throws Exception
  71. */
  72. public void resumeJob(String taskCode, String jobGroup) throws Exception{
  73. JobKey jobKey = new JobKey(taskCode, jobGroup);
  74. JobDetail jobDetail = scheduler.getJobDetail(jobKey);
  75. if(jobDetail == null){
  76. return;
  77. }
  78. scheduler.resumeJob(jobKey);
  79. }
  80. /**
  81. * 删除某个定时任务
  82. * @param taskCode
  83. * @param jobGroup
  84. * @throws Exception
  85. */
  86. public void deleteJob(String taskCode, String jobGroup) throws Exception{
  87. JobKey jobKey = new JobKey(taskCode, jobGroup);
  88. JobDetail jobDetail = scheduler.getJobDetail(jobKey);
  89. if(jobDetail == null){
  90. return;
  91. }
  92. scheduler.deleteJob(jobKey);
  93. }
  94. }
  • 编写任务类JOB

    1. 任务类JOB就是定时任务具体要处理的系统业务逻辑,需要实现Job接口。

在任务启动时,通过jobClass传入JobDetail。

  1. public class DemoJob implements Job {
  2. @Override
  3. public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
  4. String taskCode = jobExecutionContext.getJobDetail().getKey().getName();
  5. System.out.println("执行定时任务:" + taskCode);
  6. }
  7. }
  • 配置Scheduler

在Configuration中配置Scheduler实例,并启动。

  1. @Configuration
  2. public class QuartzConfig {
  3. @Bean
  4. public Scheduler scheduler(){
  5. Scheduler scheduler = null;
  6. SchedulerFactory factory = new StdSchedulerFactory();
  7. try {
  8. scheduler = factory.getScheduler();
  9. } catch (SchedulerException e) {
  10. e.printStackTrace();
  11. }
  12. if(scheduler != null){
  13. try {
  14. //启动定时任务
  15. scheduler.start();
  16. } catch (SchedulerException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. return scheduler;
  21. }
  22. }
  • 编写API接口

    1. 通过Controller提供API接口,这里的TaskService调用了QartzService的对应接口,并做了一个写数据库读写操作,主要记录定时任务状态、执行记录信息的等。
  1. @RestController
  2. @RequestMapping("/api/task")
  3. public class TaskController {
  4. @Autowired
  5. private TaskService service;
  6. @RequestMapping("/start")
  7. public Object start(int id){
  8. try {
  9. service.startJob(id);
  10. return RtnData.ok();
  11. } catch (Exception e) {
  12. return RtnData.fail(e.getMessage());
  13. }
  14. }
  15. @RequestMapping("/pause")
  16. public Object pause(int id){
  17. try {
  18. service.pauseJob(id);
  19. return RtnData.ok();
  20. } catch (Exception e) {
  21. return RtnData.fail(e.getMessage());
  22. }
  23. }
  24. @RequestMapping("/resume")
  25. public Object resume(int id){
  26. try {
  27. service.resumeJob(id);
  28. return RtnData.ok();
  29. } catch (Exception e) {
  30. return RtnData.fail(e.getMessage());
  31. }
  32. }
  33. @RequestMapping("/remove")
  34. public Object remove(int id){
  35. try {
  36. service.deleteJob(id);
  37. return RtnData.ok();
  38. } catch (Exception e) {
  39. return RtnData.fail(e.getMessage());
  40. }
  41. }
  42. }

本文转载自: https://blog.csdn.net/hk000001/article/details/143038049
版权归原作者 蓝眸少年CY 所有, 如有侵权,请联系我们删除。

“SpringBoot创建动态定时任务的几种方式”的评论:

还没有评论