Due to work reasons, it is necessary to dynamically add, delete, modify and check timed tasks. Most of the data on the Internet are realized by integrating the quertz framework. I checked some data and found that springBoot itself supports the dynamic control of timing tasks. And it is improved to support any multi parameter timing task configuration
The implementation results are as follows:
The background test is shown as follows:
The simple demo address of github is as follows:
springboot-dynamic-task
1. Configuration class of scheduled task: SchedulingConfig
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; /** * @program: simple-demo * @description: Scheduled task configuration class * @author: CaoTing * @date: 2019/5/23 **/ @Configuration public class SchedulingConfig { @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); // Number of core threads in thread pool for scheduled task execution taskScheduler.setPoolSize(4); taskScheduler.setRemoveOnCancelPolicy(true); taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-"); return taskScheduler; } }
2. Timed task registration class: crontaskregister
This class contains the core function methods of adding and removing timed tasks
import com.caotinging.demo.task.ScheduledTask; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.config.CronTask; import org.springframework.stereotype.Component; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * @program: simple-demo * @description: Add a scheduled task registration class to add or delete scheduled tasks. * @author: CaoTing * @date: 2019/5/23 **/ @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; } /** * New scheduled task * @param task * @param cronExpression */ 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)); } } /** * Remove scheduled tasks * @param task */ 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(); } }
3. Scheduled task execution class: schedulerunnable
import com.caotinging.demo.utils.SpringContextUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Method; import java.util.Objects; /** * @program: simple-demo * @description: Scheduled task operation class * @author: CaoTing * @date: 2019/5/23 **/ public class SchedulingRunnable implements Runnable { private static final Logger logger = LoggerFactory.getLogger(SchedulingRunnable.class); private String beanName; private String methodName; private Object[] params; public SchedulingRunnable(String beanName, String methodName) { this(beanName, methodName, null); } public SchedulingRunnable(String beanName, String methodName, Object...params ) { this.beanName = beanName; this.methodName = methodName; this.params = params; } @Override public void run() { logger.info("Scheduled task start execution - bean: {},Method:{},Parameters:{}", beanName, methodName, params); long startTime = System.currentTimeMillis(); try { Object target = SpringContextUtils.getBean(beanName); Method method = null; if (null != params && params.length > 0) { Class<?>[] paramCls = new Class[params.length]; for (int i = 0; i < params.length; i++) { paramCls[i] = params[i].getClass(); } method = target.getClass().getDeclaredMethod(methodName, paramCls); } else { method = target.getClass().getDeclaredMethod(methodName); } ReflectionUtils.makeAccessible(method); if (null != params && params.length > 0) { method.invoke(target, params); } else { method.invoke(target); } } catch (Exception ex) { logger.error(String.format("Timing task execution exception - bean: %s,Method:%s,Parameters:%s ", beanName, methodName, params), ex); } long times = System.currentTimeMillis() - startTime; logger.info("End of scheduled task execution - bean: {},Method:{},Parameters:{},Time consuming:{} Millisecond", 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); } }
4. Scheduled task control class: ScheduledTask
import java.util.concurrent.ScheduledFuture; /** * @program: simple-demo * @description: Scheduled task control class * @author: CaoTing * @date: 2019/5/23 **/ public final class ScheduledTask { public volatile ScheduledFuture<?> future; /** * Cancel scheduled task */ public void cancel() { ScheduledFuture<?> future = this.future; if (future != null) { future.cancel(true); } } }
5. Timing task test
Write a task class that needs to be used for testing
import org.springframework.stereotype.Component; /** * @program: simple-demo * @description: * @author: CaoTing * @date: 2019/5/23 **/ @Component("demoTask") public class DemoTask { public void taskWithParams(String param1, Integer param2) { System.out.println("This is a sample task with parameters:" + param1 + param2); } public void taskNoParams() { System.out.println("This is a nonparametric sample task"); } }
Unit test
import com.caotinging.demo.application.DynamicTaskApplication; import com.caotinging.demo.application.SchedulingRunnable; import com.caotinging.demo.config.CronTaskRegistrar; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @program: simple-demo * @description: Test scheduled tasks * @author: CaoTing * @date: 2019/5/23 **/ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = DynamicTaskApplication.class) public class TaskTest { @Autowired CronTaskRegistrar cronTaskRegistrar; @Test public void testTask() throws InterruptedException { SchedulingRunnable task = new SchedulingRunnable("demoTask", "taskNoParams", null); cronTaskRegistrar.addCronTask(task, "0/10 * * * * ?"); // Easy to observe Thread.sleep(3000000); } @Test public void testHaveParamsTask() throws InterruptedException { SchedulingRunnable task = new SchedulingRunnable("demoTask", "taskWithParams", "haha", 23); cronTaskRegistrar.addCronTask(task, "0/10 * * * * ?"); // Easy to observe Thread.sleep(3000000); } }
6. Tool class: SpringContextUtils
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * @program: simple-demo * @description: spring Get bean tool class * @author: CaoTing * @date: 2019/5/23 **/ @Component public class SpringContextUtils implements ApplicationContextAware { private static ApplicationContext applicationContext = null; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (SpringContextUtils.applicationContext == null) { SpringContextUtils.applicationContext = applicationContext; } } //Get applicationContext public static ApplicationContext getApplicationContext() { return applicationContext; } //Get Bean through name public static Object getBean(String name) { return getApplicationContext().getBean(name); } //Get Bean through class public static <T> T getBean(Class<T> clazz) { return getApplicationContext().getBean(clazz); } //Return the specified Bean through name and Clazz public static <T> T getBean(String name, Class<T> clazz) { return getApplicationContext().getBean(name, clazz); } }
7. My pom dependence
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatisplus-spring-boot-starter</artifactId> <version>1.0.5</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>2.1.9</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- data base--> <!--<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>--> <!-- https://mvnrepository.com/artifact/com.hynnet/oracle-driver-ojdbc --> <!--<dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.1.0</version> </dependency>--> <!-- unit testing --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>provided</scope> </dependency> <!--redisTemplate --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.7.3</version> </dependency> <!-- http Connect restTemplate --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient-cache</artifactId> </dependency> <!-- tool--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.31</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <!-- https://mvnrepository.com/artifact/com.google/guava --> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>10.0.1</version> </dependency> <!-- pinyin4j --> <dependency> <groupId>com.belerweb</groupId> <artifactId>pinyin4j</artifactId> <version>2.5.0</version> </dependency> </dependencies>
8. summary
It is suggested to move to github to get a simple demo for practice, at the beginning of this article. Like it if you can, refill.