SpringBoot Dynamic Timed Tasks

Time:2024-3-7

The timed task function in this article (Add, delete, change, start, pause) Without further ado, straight to the code, you guys can use it with direct CV!!!!

SpringBoot Dynamic Timed Tasks


Thread pool configuration class for executing timed tasks

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
public class SchedulingConfig {  
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        // Number of core threads in the timed task execution thread pool  
        taskScheduler.setPoolSize(6);  
        taskScheduler.setRemoveOnCancelPolicy(true);  
        taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");  
        return taskScheduler;  
    }  
}

Wrapper class for ScheduledFuture

ScheduledFuture is the execution result of the ScheduledExecutorService timed task thread pool.

import java.util.concurrent.ScheduledFuture;

public final class ScheduledTask {
  
    volatile ScheduledFuture<?> future;
    /**  
     * :: Cancellation of timed tasks  
     */  
    public void cancel() {  
        ScheduledFuture<?> future = this.future;  
        if (future != null) {  
            future.cancel(true);  
        }  
    }  
}

Runnable interface implementation class

Called by the timed task thread pool to execute the method inside the specified bean.

import com.ying.demo.utils.springContextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.util.Objects;

public class SchedulingRunnable implements Runnable {

   private static final Logger logger = LoggerFactory.getLogger(SchedulingRunnable.class);

   private String beanName;

   private String methodName;

   private String params;

   public SchedulingRunnable(String beanName, String methodName) {
      this(beanName, methodName, null);
   }

   public SchedulingRunnable(String beanName, String methodName, String params) {
      this.beanName = beanName;
      this.methodName = methodName;
      this.params = params;
   }

   @Override
   public void run() {
      logger.info("Timed task started execution - bean: {}, method: {}, parameters: {}", beanName, methodName, params);
      long startTime = System.currentTimeMillis();

      try {
         Object target = springContextUtils.getBean(beanName);

         Method method = null;
         if (!StringUtils.isEmpty(params)) {
            method = target.getClass().getDeclaredMethod(methodName, String.class);
         } else {
            method = target.getClass().getDeclaredMethod(methodName);
         }

         ReflectionUtils.makeAccessible(method);
         if (!StringUtils.isEmpty(params)) {
            method.invoke(target, params);
         } else {
            method.invoke(target);
         }
      } catch (Exception ex) {
         logger.error(String.format("Timed task execution exception - bean: %s, method: %s, parameters: %s ", beanName, methodName, params), ex);
      }

      long times = System.currentTimeMillis() - startTime;
      logger.info("End of timed task execution - bean: {}, method: {}, parameters: {}, time taken: {} milliseconds", beanName, methodName, params, times);
   }

   @Override
   public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      SchedulingRunnable that = (SchedulingRunnable) o;
      if (params == null) {
         return beanName.equals(that.beanName) &&
               methodName.equals(that.methodName) &&
               that.params == null;
      }

      return beanName.equals(that.beanName) &&
            methodName.equals(that.methodName) &&
            params.equals(that.params);
   }

   @Override
   public int hashCode() {
      if (params == null) {
         return Objects.hash(beanName, methodName);
      }

      return Objects.hash(beanName, methodName, params);
   }
}

Timed Task Registration Class

Used to add and delete timed tasks

@Component
public class CronTaskRegistrar implements DisposableBean {

   private final Map<Runnable, ScheduledTask> scheduledTasks = new ConcurrentHashMap<>(16);

   @Autowired
   private TaskScheduler taskScheduler;

   public TaskScheduler getScheduler() {
      return this.taskScheduler;
   }

   public void addCronTask(Runnable task, String cronExpression) {
      addCronTask(new CronTask(task, cronExpression));
   }

   public void addCronTask(CronTask cronTask) {
      if (cronTask != null) {
         Runnable task = cronTask.getRunnable();
         if (this.scheduledTasks.containsKey(task)) {
            removeCronTask(task);
         }

         this.scheduledTasks.put(task, scheduleCronTask(cronTask));
      }
   }

   public void removeCronTask(Runnable task) {
      ScheduledTask scheduledTask = this.scheduledTasks.remove(task);
      if (scheduledTask != null)
         scheduledTask.cancel();
   }

   public ScheduledTask scheduleCronTask(CronTask cronTask) {
      ScheduledTask scheduledTask = new ScheduledTask();
      scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());

      return scheduledTask;
   }


   @Override
   public void destroy() {
      for (ScheduledTask task : this.scheduledTasks.values()) {
         task.cancel();
      }

      this.scheduledTasks.clear();
   }
}  

Timed Task Sample Class

@Slf4j
@Component("taskDemo")
public class Task1 {  
    public void taskByParams(String params) {
        log.info("taskByParams Execution time :{}", new SimpleDateFormat("yyyy-MM-dd HH ss").format(new Date()));
        log.info("taskByParams executing example task with parameters: {}",params);;
    }  
  
    public void taskNoParams() {
        log.info("taskByParams Execution time :{}", new SimpleDateFormat("yyyy-MM-dd HH ss").format(new Date()));
        log.info("taskNoParams executing example task with no params");;
    }

    public void test(String params) {
        log.info("test execution time :{}", new SimpleDateFormat("yyyy-MM-dd HH ss").format(new Date()));
        log.info("test executing parameterized example task: {}",params);
    }
}

Database table design

CREATE TABLE `schedule_setting` (
  'job_id' int NOT NULL AUTO_INCREMENT COMMENT 'task ID',
  'bean_name' varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'bean name ',
  `method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Method name',.
  `method_params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'method_params',.
  'cron_expression' varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'cron expression ',
  'remark' varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'Remark ',
  `job_status` int DEFAULT NULL COMMENT 'Status (1 Normal 0 Suspended)',
  'create_time' datetime DEFAULT NULL COMMENT 'Create time ',
  'update_time' datetime DEFAULT NULL COMMENT 'Modify time ',
  PRIMARY KEY (`job_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

entity class

@Data
public class ScheduleSetting extends Model<ScheduleSetting> {
   /**
    * :: Mandate ID
    */
   @Id
   private Integer jobId;
   /**
    * bean name
    */
   private String beanName;
   /**
    * :: Method name
    */
   private String methodName;
   /**
    * :: Methodological parameters
    */
   private String methodParams;
   /**
    * :: cron expressions
    */
   private String cronExpression;
   /**
    * :: Status (1 normal 0 suspended)
    */
   private Integer jobStatus;
   /**
    * :: Remarks
    */
   private String remark;
   /**
    * :: Creation time
    */
   private Date createTime;
   /**
    * :: Update time
    */
   private Date updateTime;
}

Timed Task Warm-up

After the spring boot project has finished starting, load the timed tasks in the database with a status of normal

@Service  
public class SysJobRunner implements CommandLineRunner {  
  
    private static final Logger logger = LoggerFactory.getLogger(SysJobRunner.class);  
  
    @Autowired  
    private CronTaskRegistrar cronTaskRegistrar;  
  
    @Override  
    public void run(String... args) {  
        // Initially load the timed tasks in the database with a normal status  
        ScheduleSetting existedSysJob = new ScheduleSetting();
        List<ScheduleSetting> jobList = existedSysJob.selectList(new QueryWrapper<ScheduleSetting>().eq("job_status", 1));
        if (CollectionUtils.isNotEmpty(jobList)) {  
            for (ScheduleSetting job : jobList) {  
                SchedulingRunnable task = new SchedulingRunnable(job.getBeanName(), job.getMethodName(), job.getMethodParams());  
                cronTaskRegistrar.addCronTask(task, job.getCronExpression());  
            }  
            logger.info("Timed task is loaded...") ;  
        }  
    }  
}

tools

Used to get beans from the spring container

@Component  
public class SpringContextUtils implements ApplicationContextAware {  
  
    private static ApplicationContext applicationContext;  
  
    @Override  
    public void setApplicationContext(ApplicationContext applicationContext)  
            throws BeansException {  
        SpringContextUtils.applicationContext = applicationContext;  
    }  
  
    public static Object getBean(String name) {  
        return applicationContext.getBean(name);  
    }  
  
    public static <T> T getBean(Class<T> requiredType) {  
        return applicationContext.getBean(requiredType);  
    }  
  
    public static <T> T getBean(String name, Class<T> requiredType) {  
        return applicationContext.getBean(name, requiredType);  
    }  
  
    public static boolean containsBean(String name) {  
        return applicationContext.containsBean(name);  
    }  
  
    public static boolean isSingleton(String name) {  
        return applicationContext.isSingleton(name);  
    }  
  
    public static Class<? extends Object> getType(String name) {  
        return applicationContext.getType(name);  
    }  
}

Timed tasks: add/delete/change/start/pause

@RestController
public class TestController {

   @Autowired
   private CronTaskRegistrar cronTaskRegistrar;

   /**
    * :: Adding timed tasks
    *
    * @param sysJob
    * @return
    */
   @PostMapping("add")
   public boolean add(@RequestBody ScheduleSetting sysJob) {
      sysJob.setCreateTime(new Date());
      sysJob.setUpdateTime(new Date());

      boolean insert = sysJob.insert();
      if (!insert) {
         return false;
      }else {
         if (sysJob.getJobStatus().equals(1)) {// Added successfully, and the status is 1, put it directly into the tasker
            SchedulingRunnable task = new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(), sysJob.getMethodParams());
            cronTaskRegistrar.addCronTask(task, sysJob.getCronExpression());
         }
      }
      return insert;
   }

   /**
    * :: Modification of timed tasks
    *
    * @param sysJob
    * @return
    */
   @PostMapping("update")
   public boolean update(@RequestBody ScheduleSetting sysJob) {
      sysJob.setCreateTime(new Date());
      sysJob.setUpdateTime(new Date());

      // Query pre-modification tasks
      ScheduleSetting existedSysJob = new ScheduleSetting();
      existedSysJob = existedSysJob.selectOne(new QueryWrapper<ScheduleSetting>().eq("job_id", sysJob.getJobId()));
      // Modify tasks
      boolean update = sysJob.update(new UpdateWrapper<ScheduleSetting>().eq("job_id", sysJob.getJobId()));
      if (!update) {
         return false;
      } else {
         // If the modification is successful, delete the task from the tasker and re-add it.
         SchedulingRunnable task1 = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
         cronTaskRegistrar.removeCronTask(task1);
         if (sysJob.getJobStatus().equals(1)) {// If the modified job status is 1 add to the tasker
            SchedulingRunnable task = new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(), sysJob.getMethodParams());
            cronTaskRegistrar.addCronTask(task, sysJob.getCronExpression());
         }
      }
      return update;
   }

   /**
    * :: Deletion of mandates
    *
    * @param jobId
    * @return
    */
   @PostMapping("del/{jobId}")
   public boolean del(@PathVariable("jobId") Integer jobId) {
      // First look up information about the task to be deleted
      ScheduleSetting existedSysJob = new ScheduleSetting();
      existedSysJob = existedSysJob.selectOne(new QueryWrapper<ScheduleSetting>().eq("job_id", jobId));

      // Delete
      boolean del = existedSysJob.delete(new QueryWrapper<ScheduleSetting>().eq("job_id", jobId));
      if (!del)
         return false;
      else {// Delete成功时要清除定时任务器中的对应任务
         SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
         cronTaskRegistrar.removeCronTask(task);
      }
      return del;
   }

   // Stop/start tasks
   @PostMapping("changesStatus/{jobId}/{stop}")
   public boolean changesStatus(@PathVariable("jobId") Integer jobId, @PathVariable("stop") Integer stop) {
      // Modify tasks状态
      ScheduleSetting scheduleSetting = new ScheduleSetting();
      scheduleSetting.setJobStatus(stop);
      boolean job_id = scheduleSetting.update(new UpdateWrapper<ScheduleSetting>().eq("job_id", jobId));
      if (!job_id) {
         return false;
      }
      // Query modified task information
      ScheduleSetting existedSysJob = new ScheduleSetting();
      existedSysJob = existedSysJob.selectOne(new QueryWrapper<ScheduleSetting>().eq("job_id", jobId));

      // Add task if status is 1
      if (existedSysJob.getJobStatus().equals(1)) {
         SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
         cronTaskRegistrar.addCronTask(task, existedSysJob.getCronExpression());
      } else {
         // Otherwise clear the task
         SchedulingRunnable task = new SchedulingRunnable(existedSysJob.getBeanName(), existedSysJob.getMethodName(), existedSysJob.getMethodParams());
         cronTaskRegistrar.removeCronTask(task);
      }
      return true;
   }

SpringBoot Dynamic Timed Tasks


 cron

cron expression syntax:

[seconds] [minutes] [hours] [days] [months] [weeks] [years]

Note: [year] is not a required field, and [year] can be omitted, making a total of six fields

SpringBoot Dynamic Timed Tasks

Wildcard Description:

  • * Indicates all values. Example: Setting * on the Minutes field means that it will be triggered every minute.
  • ? Indicates that no value is specified. It is used in scenarios where you don’t need to care about the current value of the field. For example: to trigger an operation on the 10th of the month, but do not care about the day of the week, so you need to week position of the field set to “?” Specifically set to 0 0 0 10 * ?
  • Indicates the interval. For example, setting “10-12” on the hour means that it will be triggered at 10, 11, and 12 o’clock.
  • , Indicates that multiple values are specified, e.g. setting “MON,WED,FRI” on the week field indicates Monday, Wednesday and Friday triggering
  • / Used for incremental triggering. For example, setting ‘5/15’ on the seconds field means that the trigger will be triggered every 15 seconds starting from 5 seconds (5,20,35,50). Setting ‘1/3’ on the Day field indicates that the trigger will be triggered every three days, starting on the first of the month.
  • L Means last. In the day field, it means the last day of the month (based on the current month, and if it is February, it will also be based on whether it is a leap), and in the week field, it means Saturday, which is equivalent to “7” or “SAT”. If you add a number before the “L”, it means the last one of the data. For example, setting the format “6L” in the week field means “the last Friday of the month”.
  • W Indicates the nearest working day (Monday through Friday) to the specified date. For example, if you put “15W” in the Day field, it means the closest working day to the 15th of the month. If the 15th falls on a Saturday, then look for the nearest Friday (14th) to trigger, if the 15th is a weekday, then look for the nearest Monday (16th) to trigger. If the 15th falls on a weekday (Monday through Friday), trigger on that day. If you specify the format as “1W”, it means trigger on the nearest weekday from the 1st of the month onwards. If the 1st is a Saturday, it will trigger on the following Monday, the 3rd. (Note, “W” can only be set in front of a specific number, do not allow the interval “-“).
  • # Serial number (the day of the month), e.g. “6#3” in the week field means on the third Saturday of the month. Note that if you specify “#5”, there is no Saturday in the fifth week, the configuration will not be triggered (perfect for Mother’s Day and Father’s Day); Tip: ‘L’ and ‘W’ can be used in combination. can be used in combination. If you set “LW” on the day field, it means that it will be triggered on the last working day of the month; the setting of the week field, if you use the English letters is not case-sensitive, that is, MON and mon are the same.

Example:

Executed every 5 seconds: */5 * * * * ?

Executed at 1-minute intervals: 0 */1 * * * ?

Executed once a day at 23:00: 0 0 23 * * ?

Executed once a day at 1 a.m.: 0 0 1 * * ?

Executed once a month on the first of the month at 1:00 a.m.: 0 0 1 1 * ?

Executed once a month on the last day of the month at 23:00: 0 0 23 L * ?

Implemented once a week on Saturdays at 1:00 a.m.: 0 0 1 ? * L

Executed once at 26, 29, 33: 0 26,29,33 * * * ?

It is executed once a day at 0:00, 13:00, 18:00 and 21:00: 0 0 0,13,18,21 * * ?

cron online expression generator:https://cron.12qqe2.com/

This is my learning in the development of the use and summarization of the small Demo, which may also exist in the middle of the shortcomings, I hope to be able to get everyone’s understanding and suggestions. If any infringement contact me!

Recommended Today

Quantum computing frameworks and libraries TensorFlow Quantum, PyTorch Quantum can be used for deep learning

catalogs 1. Install TensorFlow Quantum:2. Install PyTorch Quantum:3. Case study: quantum linear regression How to install TensorFlow Quantum and PyTorch Quantum with some simple code examples. Please note that since quantum computing is still in the early stages of development, these tools and libraries may change over time 1. Install TensorFlow Quantum: First, you need […]