SpringBoot創(chuàng)建動態(tài)定時任務的幾種方式小結(jié)
一、使用 @Scheduled 注解
@Scheduled 是 Spring 提供的一個注解,用于標記需要定時執(zhí)行的方法。常見的屬性包括:
- cron :通過 Cron 表達式定義任務的執(zhí)行時間。
- fixedRate :定義任務的固定執(zhí)行頻率,以毫秒為單位。
- fixedDelay :定義任務在前一次執(zhí)行完畢后延遲多少毫秒再執(zhí)行。
代碼示例:
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class ScheduledTasks { @Scheduled(cron = "0 0 * * * ?") // 每小時整點執(zhí)行一次 public void reportCurrentTime() { System.out.println("現(xiàn)在時間:" + System.currentTimeMillis()); } @Scheduled(fixedRate = 5000) // 每5秒執(zhí)行一次 public void fixedRateTask() { System.out.println("每5秒執(zhí)行一次任務:" + System.currentTimeMillis()); } @Scheduled(fixedDelay = 7000) // 前一次執(zhí)行完畢后延遲7秒執(zhí)行 public void fixedDelayTask() { System.out.println("延遲7秒后執(zhí)行任務:" + System.currentTimeMillis()); } }
@Scheduled 適用于大多數(shù)簡單的定時任務場景,如定時發(fā)送郵件或生成報告等。然而,它的靈活性較差,對于復雜的任務調(diào)度需求,或需要動態(tài)調(diào)整任務時間的場景,可能并不適用。
二、使用 SchedulingConfigurer 接口
SchedulingConfigurer 接口允許我們通過編程方式配置任務調(diào)度器(TaskScheduler)。通過實現(xiàn)這個接口,我們可以靈活地設(shè)置任務的調(diào)度規(guī)則,甚至動態(tài)地添加或移除任務。
簡單使用代碼:
public class TaskConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { scheduledTaskRegistrar.addTriggerTask( //1.添加任務內(nèi)容(Runnable) () -> System.out.println("執(zhí)行定時任務2: " + LocalDateTime.now().toLocalTime()), //2.設(shè)置執(zhí)行周期(Trigger) triggerContext -> { //2.1 從數(shù)據(jù)庫獲取執(zhí)行周期 String cron = zhyMapper.getCron(); //2.2 合法性校驗. if (StringUtils.isEmpty(cron)) { // Omitted Code .. } //2.3 返回執(zhí)行周期(Date) return new CronTrigger(cron).nextExecutionTime(triggerContext); } ); } }
詳細增刪該查操作:
- 動態(tài)注冊bean
public class ApplicationContextUtils implements ApplicationContextAware { private static ApplicationContext CONTEXT; /** * 設(shè)置spring上下文 * @param ctx spring上下文 * @throws BeansException * */ @Override public void setApplicationContext(ApplicationContext ctx) throws BeansException { CONTEXT = ctx; } /** * 獲取容器 * @return */ public static ApplicationContext getApplicationContext() { return CONTEXT; } /** * 獲取容器對象 * @param type * @param <T> * @return */ public static <T> T getBean(Class<T> type) { return CONTEXT.getBean(type); } public static <T> T getBean(String name,Class<T> clazz){ return CONTEXT.getBean(name, clazz); } public static Object getBean(String name){ return CONTEXT.getBean(name); } /** * springboot動態(tài)注冊bean * @param clazz * @param <T> * @return */ public static <T> T register(Class<T> clazz) { ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) ApplicationContextUtils.getApplicationContext(); DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory(); BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz); if(defaultListableBeanFactory.getBeanNamesForType(clazz).length > 0) { return defaultListableBeanFactory.getBean(clazz); } defaultListableBeanFactory.registerBeanDefinition(clazz.getName(), beanDefinitionBuilder.getRawBeanDefinition()); return (T) ApplicationContextUtils.getBean(clazz.getName()); } }
- 任務實體
public class Job { /** * 任務id, 用于標識,默認使用全限定類名 */ private String jobId; /** * 任務名稱, 默認簡單類名 */ private String jobName; /** * cron表達式, 修改后即可生效 */ private String cron; /** * 任務描述 */ private String description; /** * 是否啟用, 默認啟用, 修改后即可生效 */ private boolean enable = true; /** * 是否處于等待執(zhí)行下個任務的狀態(tài) */ private boolean active; /** * 任務運行類 */ private Class<? extends Runnable> clazz; }
- 操作類
public class JobHandler { private ScheduledTask scheduledTask; private TriggerTask triggerTask; private TriggerContext triggerContext; }
- 配置SchedulingConfigurer
public class JobSchedulingConfigurer implements SchedulingConfigurer { private ScheduledTaskRegistrar registrar; /** * 線程池任務調(diào)度器 */ @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(Runtime.getRuntime().availableProcessors() / 3 + 1); scheduler.setThreadNamePrefix("TaskScheduler-"); scheduler.setRemoveOnCancelPolicy(true); // 保證能立刻丟棄運行中的任務 taskScheduler = scheduler; // 獲取 句柄,方便后期獲取 future return scheduler; } @Override public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { scheduledTaskRegistrar.setTaskScheduler(taskScheduler()); this.registrar = scheduledTaskRegistrar; } public ScheduledTaskRegistrar getRegistrar() { return registrar; } public void setRegistrar(ScheduledTaskRegistrar registrar) { this.registrar = registrar; } }
- 增刪改查
public class SchedulerManager { /** * 任務容器 */ private Map<Job, JobHandler> tasks = new ConcurrentHashMap<>(); /** * 任務注冊 */ @Autowired private JobSchedulingConfigurer register; /** * 新增任務, 自生效 * @param job 任務實體 * @return 返回新增的任務 */ public Job addJob(Job job) { Assert.notNull(job, "job can't be null"); ScheduledTaskRegistrar registrar = register.getRegistrar(); Runnable runnable = ApplicationContextUtils.register(job.getClazz()); if(job.getJobId() == null || "".equals(job.getJobId())) { job.setJobId(job.getClazz().getName()); } Assert.isNull(this.getJob(job.getJobId()), "任務[" + job.getJobId() + "]已存在"); if(job.getJobName() == null || "".equals(job.getJobName())) { job.setJobName(ClassUtils.getShortName(job.getClazz())); } CronExpress cron = AnnotationUtils.findAnnotation(job.getClazz(), CronExpress.class); if(cron != null && !"".equals(cron.value())) { // 注解的屬性,大于配置的屬性,方便調(diào)試 job.setCron(cron.value()); } job.setEnable(true); job.setActive(true); JobHandler entity = new JobHandler(); TriggerTask triggerTask = new TriggerTask(runnable, (TriggerContext triggerContext) -> { // 每次任務執(zhí)行均會進入此方法 CronTrigger trigger = new CronTrigger(job.getCron()); entity.setTriggerContext(triggerContext); return job.isEnable() ? trigger.nextExecutionTime(triggerContext) : null; }); ScheduledTask scheduledTask = registrar.scheduleTriggerTask(triggerTask); entity.setScheduledTask(scheduledTask); entity.setTriggerTask(triggerTask); tasks.put(job, entity); return job; } /** * 任務類(必須標注了@CronExpress注解,且實現(xiàn)了Runnable接口) * @param clazz 接口類 * @return 任務對象 */ public Job addJob(Class<? extends Runnable> clazz) { Job job = new Job(); job.setClazz(clazz); return this.addJob(job); } /** * 獲取任務操作對象 * @param jobId 任務id * @return 任務操作對象 */ public JobHandler getJobHandler(String jobId) { return tasks.get(new Job(jobId)); } /** * 根據(jù)任務id獲取任務 * @param jobId 任務id * @return 任務實體 */ public Job getJob(String jobId) { Assert.hasText(jobId, "jobId can't be null"); Set<Job> jobs = tasks.keySet(); if(jobs.size() == 0) { return null; } Iterator<Job> iterator = jobs.iterator(); while (iterator.hasNext()) { Job next = iterator.next(); if(jobId.equals(next.getJobId())) { return next; } } return null; } /** * 關(guān)閉任務(若任務正在執(zhí)行,待任務執(zhí)行完) * @param jobId 任務id * @return 是否關(guān)閉成功 */ public boolean shutDown(String jobId) { try { JobHandler handler = this.getJobHandler(jobId); Assert.notNull(handler, "任務[" + jobId + "]不存在"); handler.getScheduledTask().cancel(); Job job = getJob(jobId); job.setActive(false); return true; } catch (Exception e) { return false; } } /** * 啟動已經(jīng)注冊的任務 * @param jobId 任務id * @return 是否成功啟動 */ public boolean startUp(String jobId) { try { JobHandler handler = this.getJobHandler(jobId); Assert.notNull(handler, "任務[" + jobId + "]不存在"); register.getRegistrar().scheduleTriggerTask(handler.getTriggerTask()); Job job = getJob(jobId); job.setActive(true); return true; } catch (Exception e) { return false; } } /** * 獲取所有的任務實體 * @return */ public List<Job> getJobs() { return new ArrayList<>(tasks.keySet()); } /** * 刪除任務,先關(guān)閉再刪除 * @param jobId * @return */ public boolean deleteJob(String jobId) { try { Job job = this.getJob(jobId); Assert.notNull(job, "任務[" + jobId + "]不存在"); shutDown(jobId); tasks.remove(job); return true; } catch (Exception e) { return false; } } }
- 任務運行類
@CronExpress("0/1 * * * * *") public class HelloJob implements Runnable { @Override public void run() { System.out.println("hello msg" + Thread.currentThread().getId()); } }
- 測試
ConfigurableApplicationContext ctx = SpringApplication.run(UnifiedTaskScheduleStarterApplication.class, args); SchedulerManager schedulerManager = ctx.getBean(SchedulerManager.class); Job job = schedulerManager.addJob(com.github.softwarevax.web.HelloJob.class); schedulerManager.shutDown(job.getJobId());
三、使用 TaskScheduler
TaskScheduler 是Spring提供的用于調(diào)度任務的核心接口。通過 TaskScheduler,你可以靈活地安排任務的執(zhí)行時間,并且可以在運行時動態(tài)地創(chuàng)建、取消任務。
代碼示例簡單使用:
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 TaskSchedulerConfig { @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(5); scheduler.setThreadNamePrefix("MyScheduler-"); return scheduler; } }
在使用 TaskScheduler 時,可以通過 schedule 方法動態(tài)安排任務:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.TaskScheduler; import org.springframework.stereotype.Component; import java.util.Date; @Component public class DynamicTask { @Autowired private TaskScheduler taskScheduler; public void scheduleTask() { taskScheduler.schedule(() -> System.out.println("動態(tài)任務執(zhí)行:" + System.currentTimeMillis()), new Date(System.currentTimeMillis() + 5000)); } }
spring boot使用TaskScheduler實現(xiàn)動態(tài)增刪啟停定時任務
- SchedulingConfig:添加執(zhí)行定時任務的線程池配置類
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(); // 定時任務執(zhí)行線程池核心線程數(shù) taskScheduler.setPoolSize(4); taskScheduler.setRemoveOnCancelPolicy(true); taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-"); return taskScheduler; } }
- ScheduledTask:添加ScheduledFuture的包裝類
ScheduledFuture是ScheduledExecutorService定時任務線程池的執(zhí)行結(jié)果。
import java.util.concurrent.ScheduledFuture; public final class ScheduledTask { volatile ScheduledFuture<?> future; /** * 取消定時任務 */ public void cancel() { ScheduledFuture<?> future = this.future; if (future != null) { future.cancel(true); } } }
- SchedulingRunnable:添加Runnable接口實現(xiàn)類
添加Runnable接口實現(xiàn)類,被定時任務線程池調(diào)用,用來執(zhí)行指定bean里面的方法
import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Method; import java.util.Objects; public class SchedulingRunnable implements Runnable { private static final Logger logger = LoggerFactory.getLogger(SchedulingRunnable.class); private final String beanName; private final String methodName; private final 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("定時任務開始執(zhí)行 - bean:{},方法:{},參數(shù):{}", beanName, methodName, params); long startTime = System.currentTimeMillis(); try { Object target = SpringContextUtils.getBean(beanName); Method method = null; if (StringUtils.isNotEmpty(params)) { method = target.getClass().getDeclaredMethod(methodName, String.class); } else { method = target.getClass().getDeclaredMethod(methodName); } ReflectionUtils.makeAccessible(method); if (StringUtils.isNotEmpty(params)) { method.invoke(target, params); } else { method.invoke(target); } } catch (Exception ex) { logger.error(String.format("定時任務執(zhí)行異常 - bean:%s,方法:%s,參數(shù):%s ", beanName, methodName, params), ex); } long times = System.currentTimeMillis() - startTime; logger.info("定時任務執(zhí)行結(jié)束 - bean:{},方法:{},參數(shù):{},耗時:{} 毫秒", 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); } }
- CronTaskRegistrar:添加定時任務注冊類,用來增加、刪除定時任務
import com.example.testspringboot.cron.ScheduleResult; 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.*; import java.util.concurrent.ConcurrentHashMap; /** * 添加定時任務注冊類,用來增加、刪除定時任務。 */ @Component public class CronTaskRegistrar implements DisposableBean { private final Map<Runnable, ScheduledTask> scheduledTasks = new ConcurrentHashMap<>(16); private final Map<Integer, ScheduleResult> schedulerJob = new HashMap<>(); @Autowired private TaskScheduler taskScheduler; public TaskScheduler getScheduler() { return this.taskScheduler; } public void addCronTask(ScheduleResult scheduleResult) { SchedulingRunnable task = new SchedulingRunnable(scheduleResult.getBeanName(), scheduleResult.getMethodName(), scheduleResult.getMethodParams()); String cronExpression = scheduleResult.getCronExpression(); CronTask cronTask = new CronTask(task, cronExpression); // 如果當前包含這個任務,則移除 if (this.scheduledTasks.containsKey(task)) { removeCronTask(scheduleResult.getBeanName(), scheduleResult.getMethodName(), scheduleResult.getMethodParams()); } schedulerJob.put(scheduleResult.getJobId(), scheduleResult); this.scheduledTasks.put(task, scheduleCronTask(cronTask)); } public void removeCronTask(String beanName, String methodName, String methodParams) { SchedulingRunnable task = new SchedulingRunnable(beanName, methodName, methodParams); ScheduledTask scheduledTask = this.scheduledTasks.remove(task); if (scheduledTask != null) { scheduledTask.cancel(); } } public void removeCronTask(ScheduleResult scheduleResult) { schedulerJob.put(scheduleResult.getJobId(), scheduleResult); removeCronTask(scheduleResult.getBeanName(), scheduleResult.getMethodName(), scheduleResult.getMethodParams()); } public ScheduledTask scheduleCronTask(CronTask cronTask) { ScheduledTask scheduledTask = new ScheduledTask(); scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger()); return scheduledTask; } public Map<Runnable, ScheduledTask> getScheduledTasks() { return scheduledTasks; } public Map<Integer, ScheduleResult> getSchedulerJob() { return schedulerJob; } @Override public void destroy() { for (ScheduledTask task : this.scheduledTasks.values()) { task.cancel(); } this.scheduledTasks.clear(); } public ScheduleResult getSchedulerByJobId(Integer jobId) { for (ScheduleResult job : findAllTask()) { if (jobId.equals(job.getJobId())) { return job; } } return null; } public List<ScheduleResult> findAllTask() { List<ScheduleResult> ScheduleResults = new ArrayList<>(); Set<Map.Entry<Integer, ScheduleResult>> entries = schedulerJob.entrySet(); for (Map.Entry<Integer, ScheduleResult> en : entries) { ScheduleResults.add(en.getValue()); } return ScheduleResults; } }
- CronUtils:校驗Cron表達式的有效性
import org.springframework.scheduling.support.CronExpression; public class CronUtils { /** * 返回一個布爾值代表一個給定的Cron表達式的有效性 * * @param cronExpression Cron表達式 * @return boolean 表達式是否有效 */ public static boolean isValid(String cronExpression) { return CronExpression.isValidExpression(cronExpression); } }
- ScheduleResult:添加定時任務實體類
import lombok.Data; @Data public class ScheduleResult { /** * 任務ID */ private Integer jobId; /** * bean名稱 */ private String beanName; /** * 方法名稱 */ private String methodName; /** * 方法參數(shù): 執(zhí)行service里面的哪一種方法 */ private String methodParams; /** * cron表達式 */ private String cronExpression; /** * 狀態(tài)(1正常 0暫停) */ private Integer jobStatus; /** * 備注 */ private String remark; /** * 創(chuàng)建時間 */ private String createTime; /** * 更新時間 */ private String updateTime; }
- ScheduleJobStatus:任務狀態(tài)枚舉類型
public enum ScheduleJobStatus { /** * 暫停 */ PAUSE, /** * 正常 */ NORMAL; }
- SpringContextUtils類:從spring容器里獲取bean
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @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; } } public static ApplicationContext getApplicationContext() { return applicationContext; } // 通過name獲取 Bean. public static Object getBean(String name) { return getApplicationContext().getBean(name); } // 通過class獲取Bean. public static <T> T getBean(Class<T> clazz) { return getApplicationContext().getBean(clazz); } // 通過name,以及Clazz返回指定的Bean public static <T> T getBean(String name, Class<T> clazz) { return getApplicationContext().getBean(name, clazz); } public static boolean containsBean(String name) { return getApplicationContext().containsBean(name); } public static boolean isSingleton(String name) { return getApplicationContext().isSingleton(name); } public static Class<? extends Object> getType(String name) { return getApplicationContext().getType(name); } }
- ScheduleJobService:增刪啟停service方法
@Service @Slf4j public class ScheduleJobService { @Autowired private CronTaskRegistrar cronTaskRegistrar; public void addScheduleJob(ScheduleResult scheduleResult) { long currentTimeMillis = System.currentTimeMillis(); scheduleResult.setCreateTime(formatTimeYMD_HMS_SSS(currentTimeMillis)); scheduleResult.setUpdateTime(formatTimeYMD_HMS_SSS(currentTimeMillis)); scheduleResult.setJobId(findAllTask().size() + 1); if (scheduleResult.getJobStatus().equals(ScheduleJobStatus.NORMAL.ordinal())) { log.info("Stop or pause: is now on"); cronTaskRegistrar.addCronTask(scheduleResult); return; } cronTaskRegistrar.getSchedulerJob().put(scheduleResult.getJobId(), scheduleResult); } public void editScheduleJob(ScheduleResult currentSchedule) { //先移除 cronTaskRegistrar.removeCronTask(currentSchedule.getBeanName(), currentSchedule.getMethodName(), currentSchedule.getMethodParams()); ScheduleResult pastScheduleJob = cronTaskRegistrar.getSchedulerByJobId(currentSchedule.getJobId()); if (pastScheduleJob == null) { System.out.println("沒有這個任務"); return; } //然后判斷是否開啟, 如果開啟的話,現(xiàn)在立即執(zhí)行 startOrStopSchedulerJob(currentSchedule, true); } public void deleteScheduleJob(ScheduleResult scheduleResult) { // 清除這個任務 cronTaskRegistrar.removeCronTask(scheduleResult.getBeanName(), scheduleResult.getMethodName(), scheduleResult.getMethodParams()); // 清除這個任務的數(shù)據(jù) cronTaskRegistrar.getSchedulerJob().remove(scheduleResult.getJobId()); } public void startOrStopScheduler(ScheduleResult scheduleResult) { cronTaskRegistrar.getSchedulerJob().get(scheduleResult.getJobId()).setJobStatus(scheduleResult.getJobStatus()); startOrStopSchedulerJob(scheduleResult, false); } private void startOrStopSchedulerJob(ScheduleResult scheduleResult, boolean update) { // 更新時間 scheduleResult.setUpdateTime(formatTimeYMD_HMS_SSS(System.currentTimeMillis())); if (scheduleResult.getJobStatus().equals(ScheduleJobStatus.NORMAL.ordinal())) { System.out.println("停止或暫停:現(xiàn)在是開啟"); cronTaskRegistrar.addCronTask(scheduleResult); return; } System.out.println("停止或暫停:現(xiàn)在是暫停"); if (update){ cronTaskRegistrar.removeCronTask(scheduleResult); return; } cronTaskRegistrar.removeCronTask(scheduleResult.getBeanName(), scheduleResult.getMethodName(), scheduleResult.getMethodParams()); } public List<ScheduleResult> findAllTask() { return cronTaskRegistrar.findAllTask(); } // 轉(zhuǎn)換為年-月-日 時:分:秒 private String formatTimeYMD_HMS_SSS(long time) { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(time); } }
- cronController:訪問接口
import com.example.testspringboot.cron.ScheduleResult; import com.example.testspringboot.cron.ScheduleJobService; import com.example.testspringboot.cron.utils.CronUtils; import com.google.gson.Gson; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController public class cronController { @Autowired private ScheduleJobService scheduleJobService; /** * 測試上傳的用例文件, 獲取詳細執(zhí)行結(jié)果 */ @PostMapping("/add") void executeTestOneFile(@RequestBody ScheduleResult scheduleResult) { boolean valid = CronUtils.isValid(scheduleResult.getCronExpression()); if (valid){ System.out.println("校驗成功, 添加任務"); scheduleResult.setMethodParams(scheduleResult.getBranch()+scheduleResult.getCaseDir()); scheduleJobService.addScheduleJob(scheduleResult); }else { System.out.println("校驗失敗"); } } @PostMapping("/stop") void end(@RequestBody ScheduleResult scheduleResult) { Gson gson = new Gson(); System.out.println("================"); System.out.println(scheduleResult); System.out.println("================="); scheduleResult.setJobStatus(0); scheduleJobService.startOrStopScheduler(scheduleResult); } @PostMapping("/start") void start(@RequestBody ScheduleResult scheduleResult) { System.out.println("================"); System.out.println(scheduleResult); System.out.println("================="); scheduleResult.setJobStatus(1); scheduleJobService.startOrStopScheduler(scheduleResult); } @PostMapping("/edit") void edit(@RequestBody ScheduleResult scheduleResult) { System.out.println("=======edit========="); System.out.println(scheduleResult); System.out.println("================="); scheduleJobService.editScheduleJob(scheduleResult); } @PostMapping("/delete") void delete(@RequestBody ScheduleResult scheduleResult) { System.out.println("=======delete========="); System.out.println(scheduleResult); System.out.println("================="); scheduleJobService.deleteScheduleJob(scheduleResult); } @GetMapping("/tasks") List<ScheduleResult> get() throws Exception { List<ScheduleResult> allTask = scheduleJobService.findAllTask(); System.out.println("現(xiàn)在的定時任務數(shù)量 = " + allTask.size()); System.out.println("現(xiàn)在的定時任務 = " + allTask); return allTask; } }
- 測試bean
import org.springframework.stereotype.Component; @Component public class c1 { public void test1(String y){ System.out.println("這個是test1的bean : " + y); } public void test2(){ System.out.println("這個是test1的bean中test2方法"); } }
- 項目啟動后的定時任務
import com.example.testspringboot.cron.ScheduleJobService; import com.example.testspringboot.cron.ScheduleResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class init implements CommandLineRunner { @Autowired private ScheduleJobService scheduleJobService; @Override public void run(String... args) throws Exception { System.out.println("開始珍惜"); ScheduleResult scheduleResult = new ScheduleResult(); scheduleResult.setBeanName("c1"); scheduleResult.setMethodName("test1"); scheduleResult.setCronExpression("0/25 * * * * *"); scheduleResult.setJobStatus(1); scheduleResult.setMethodParams("test1"); scheduleJobService.addScheduleJob(scheduleResult); scheduleJobService.findAllTask(); } }
四、使用 Quartz 實現(xiàn)定時任務
Quartz 是一個功能強大的開源任務調(diào)度框架,支持復雜的任務調(diào)度需求,如任務的持久化、分布式任務管理、基于數(shù)據(jù)庫的調(diào)度等。Spring Boot 提供了對 Quartz 的良好集成,使得在應用中使用 Quartz 變得更加簡單。
簡單使用:
import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.JobDetailFactoryBean; import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean; @Configuration public class QuartzConfig { @Bean public JobDetailFactoryBean jobDetail() { JobDetailFactoryBean factoryBean = new JobDetailFactoryBean(); factoryBean.setJobClass(SampleJob.class); factoryBean.setDescription("Sample Quartz Job"); factoryBean.setDurability(true); return factoryBean; } @Bean public SimpleTriggerFactoryBean trigger(JobDetail jobDetail) { SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean(); factoryBean.setJobDetail(jobDetail); factoryBean.setRepeatInterval(5000); // 每5秒執(zhí)行一次 factoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY); return factoryBean; } public static class SampleJob implements Job { @Override public void execute(JobExecutionContext context) { System.out.println("Quartz任務執(zhí)行:" + System.currentTimeMillis()); } } }
springboot整合
- 數(shù)據(jù)庫設(shè)計
將任務計劃放入數(shù)據(jù)庫中保存。在啟動任務是,從數(shù)據(jù)庫中查找任務計劃信息,并動態(tài)配置進去即可。
DROP TABLE IF EXISTS `cc_task_info`; CREATE TABLE `cc_task_info` ( `TID` int(11) NOT NULL AUTO_INCREMENT, `TASK_ANME` varchar(50) NOT NULL, `TASK_CODE` varchar(50) NOT NULL, `JOB_CLASS` varchar(200) NOT NULL, `JOB_GROUP` varchar(50) NOT NULL, `CRON` varchar(50) NOT NULL, `DEL_STATUS` varchar(2) DEFAULT '1' NULL, `CRT_TIME` datetime DEFAULT NULL, PRIMARY KEY (`TID`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='定時任務管理表'; DROP TABLE IF EXISTS `cc_task_record`; CREATE TABLE `cc_task_record` ( `RID` int(11) NOT NULL AUTO_INCREMENT, `TASK_CODE` varchar(50) NOT NULL, `RUN_TIME` datetime NOT NULL, `RUN_CODE` char(1) NOT NULL, `RUN_MSG` varchar(100) NULL, PRIMARY KEY (`RID`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='定時任務運行記錄表'; DROP TABLE IF EXISTS `cc_task_status`; CREATE TABLE `cc_task_status` ( `TASK_CODE` varchar(50) NOT NULL, `TASK_STATUS` varchar(10) NOT NULL, `LST_SUCC_TIME` datetime NOT NULL, `LST_TIME` datetime NOT NULL, PRIMARY KEY (`TASK_CODE`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='定時任務運行狀態(tài)表';
- 定時任務管理
通過Scheduler的方法來實現(xiàn)。Scheduler提供了一系列方法來管理定時任務的執(zhí)行狀態(tài)。
主要包括:
- scheduleJob():添加定時任務
- rescheduleJob():修改定時任務
- pauseJob():暫停定時任務執(zhí)行
- resumeJob():恢復定時任務執(zhí)行
- deleteJob():刪除定時任務執(zhí)行
針對上述方法,只需要傳入對應參數(shù)即可。建了一個QuartzService來管理定時任務,供業(yè)務層調(diào)用。
詳細代碼如下:
/** * 定時任務管理服務 */ @Service public class QuartzService { public static String SCHEDULER_OPR_START = "start"; public static String SCHEDULER_OPR_PAUSE = "pause"; public static String SCHEDULER_OPR_RESUME = "resume"; public static String SCHEDULER_OPR_REMOVE = "remove"; @Autowired private Scheduler scheduler; /** * 啟動任務 */ public void startJob(String taskCode, String taskAnme, String cron, String jobGroup, String className) throws Exception{ Class<Job> jobClass = null; try { jobClass = (Class<Job>) Class.forName(className);//獲取任務執(zhí)行類 } catch (ClassNotFoundException e) { throw new Exception("任務類不存在"); } //創(chuàng)建job,指定job名稱和分組 JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(taskCode, jobGroup).build(); //創(chuàng)建表達式工作計劃 CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron); //創(chuàng)建觸發(fā)器 CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(taskCode, jobGroup) .withSchedule(cronScheduleBuilder).build(); scheduler.scheduleJob(jobDetail, cronTrigger); } /** * 修改定時任務執(zhí)行時間 * @param taskCode * @param jobGroup * @param cron 新的時間 * @throws Exception */ public void modifyJob(String taskCode, String jobGroup, String cron) throws Exception{ TriggerKey triggerKey = new TriggerKey(taskCode, jobGroup); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); String oldCron = trigger.getCronExpression(); if(!oldCron.equals(cron)){ CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(taskCode, jobGroup) .withSchedule(CronScheduleBuilder.cronSchedule(cron)).build(); Date date = scheduler.rescheduleJob(triggerKey, cronTrigger); if(date == null){ throw new Exception("修改定時任務執(zhí)行時間報錯"); } } } /** * 暫停某個定時任務(任務恢復后,暫停時間段內(nèi)未執(zhí)行的任務會繼續(xù)執(zhí)行,如暫停時間段內(nèi)有2次,則會執(zhí)行2次) * @param taskCode * @param jobGroup * @throws Exception */ public void pauseJob(String taskCode, String jobGroup) throws Exception{ JobKey jobKey = new JobKey(taskCode, jobGroup); JobDetail jobDetail = scheduler.getJobDetail(jobKey); if(jobDetail == null){ return; } scheduler.pauseJob(jobKey); } /** * 恢復某個定時任務 * @param taskCode * @param jobGroup * @throws Exception */ public void resumeJob(String taskCode, String jobGroup) throws Exception{ JobKey jobKey = new JobKey(taskCode, jobGroup); JobDetail jobDetail = scheduler.getJobDetail(jobKey); if(jobDetail == null){ return; } scheduler.resumeJob(jobKey); } /** * 刪除某個定時任務 * @param taskCode * @param jobGroup * @throws Exception */ public void deleteJob(String taskCode, String jobGroup) throws Exception{ JobKey jobKey = new JobKey(taskCode, jobGroup); JobDetail jobDetail = scheduler.getJobDetail(jobKey); if(jobDetail == null){ return; } scheduler.deleteJob(jobKey); } }
- 編寫任務類JOB
任務類JOB就是定時任務具體要處理的系統(tǒng)業(yè)務邏輯,需要實現(xiàn)Job接口。
在任務啟動時,通過jobClass傳入JobDetail。
public class DemoJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { String taskCode = jobExecutionContext.getJobDetail().getKey().getName(); System.out.println("執(zhí)行定時任務:" + taskCode); } }
- 配置Scheduler
在Configuration中配置Scheduler實例,并啟動。
@Configuration public class QuartzConfig { @Bean public Scheduler scheduler(){ Scheduler scheduler = null; SchedulerFactory factory = new StdSchedulerFactory(); try { scheduler = factory.getScheduler(); } catch (SchedulerException e) { e.printStackTrace(); } if(scheduler != null){ try { //啟動定時任務 scheduler.start(); } catch (SchedulerException e) { e.printStackTrace(); } } return scheduler; } }
- 編寫API接口
通過Controller提供API接口,這里的TaskService調(diào)用了QartzService的對應接口,并做了一個寫數(shù)據(jù)庫讀寫操作,主要記錄定時任務狀態(tài)、執(zhí)行記錄信息的等。
@RestController @RequestMapping("/api/task") public class TaskController { @Autowired private TaskService service; @RequestMapping("/start") public Object start(int id){ try { service.startJob(id); return RtnData.ok(); } catch (Exception e) { return RtnData.fail(e.getMessage()); } } @RequestMapping("/pause") public Object pause(int id){ try { service.pauseJob(id); return RtnData.ok(); } catch (Exception e) { return RtnData.fail(e.getMessage()); } } @RequestMapping("/resume") public Object resume(int id){ try { service.resumeJob(id); return RtnData.ok(); } catch (Exception e) { return RtnData.fail(e.getMessage()); } } @RequestMapping("/remove") public Object remove(int id){ try { service.deleteJob(id); return RtnData.ok(); } catch (Exception e) { return RtnData.fail(e.getMessage()); } } }
以上就是SpringBoot創(chuàng)建動態(tài)定時任務的幾種方式小結(jié)的詳細內(nèi)容,更多關(guān)于SpringBoot動態(tài)定時任務的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Springboot集成knife4j實現(xiàn)風格化API文檔
這篇文章主要介紹了Springboot如何集成knife4j實現(xiàn)風格化API文檔,幫助大家更好的使用springboot框架,感興趣的朋友可以了解下2020-12-12mybatis plus自動生成代碼tinyint(1)自動轉(zhuǎn)換為Boolean的問題及解決
這篇文章主要介紹了mybatis plus自動生成代碼tinyint(1)自動轉(zhuǎn)換為Boolean的問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08springboot中EasyPoi實現(xiàn)自動新增序號的方法
本文主要介紹了EasyPoi實現(xiàn)自動新增序號,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09