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

目录

一、使用?@Scheduled?注解

二、使用?SchedulingConfigurer?接口

三、使用?TaskScheduler

四、使用?Quartz?实现定时任务


一、使用@Scheduled注解

@Scheduled是 Spring 提供的一个注解,用于标记需要定时执行的方法。常见的属性包括:

  • cron:通过 Cron 表达式定义任务的执行时间。
  • fixedRate:定义任务的固定执行频率,以毫秒为单位。
  • fixedDelay:定义任务在前一次执行完毕后延迟多少毫秒再执行。

代码示例:

复制代码
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledTasks {

    @Scheduled(cron = "0 0 * * * ?") // 每小时整点执行一次
    public void reportCurrentTime() {
        System.out.println("现在时间:" + System.currentTimeMillis());
    }

    @Scheduled(fixedRate = 5000) // 每5秒执行一次
    public void fixedRateTask() {
        System.out.println("每5秒执行一次任务:" + System.currentTimeMillis());
    }

    @Scheduled(fixedDelay = 7000) // 前一次执行完毕后延迟7秒执行
    public void fixedDelayTask() {
        System.out.println("延迟7秒后执行任务:" + System.currentTimeMillis());
    }
}

@Scheduled适用于大多数简单的定时任务场景,如定时发送邮件或生成报告等。然而,它的灵活性较差,对于复杂的任务调度需求,或需要动态调整任务时间的场景,可能并不适用。

二、使用SchedulingConfigurer接口

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

简单使用代码:

复制代码
public class TaskConfig implements SchedulingConfigurer {
    
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.addTriggerTask(
                //1.添加任务内容(Runnable)
                () -> System.out.println("执行定时任务2: " + LocalDateTime.now().toLocalTime()),
                //2.设置执行周期(Trigger)
                triggerContext -> {
                    //2.1 从数据库获取执行周期
                    String cron = zhyMapper.getCron();
                    //2.2 合法性校验.
                    if (StringUtils.isEmpty(cron)) {
                        // Omitted Code ..
                    }
                    //2.3 返回执行周期(Date)
                    return new CronTrigger(cron).nextExecutionTime(triggerContext);
                }
        );
 
    }
}

详细增删该查操作:

  • 动态注册bean

    public class ApplicationContextUtils implements ApplicationContextAware {

    复制代码
    private static ApplicationContext CONTEXT;
    /**
     * 设置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动态注册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;
    
    /**
     * 是否处于等待执行下个任务的状态
     */
    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;
    
    /**
     * 线程池任务调度器
     */
    @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())) {
            // 注解的属性,大于配置的属性,方便调试
            job.setCron(cron.value());
        }
        job.setEnable(true);
        job.setActive(true);
        JobHandler entity = new JobHandler();
        TriggerTask triggerTask = new TriggerTask(runnable, (TriggerContext triggerContext) -> {
            // 每次任务执行均会进入此方法
            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注解,且实现了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));
    }
    
    /**
     * 根据任务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;
    }
    
    /**
     * 关闭任务(若任务正在执行,待任务执行完)
     * @param jobId 任务id
     * @return 是否关闭成功
     */
    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;
        }
    }
    
    /**
     * 启动已经注册的任务
     * @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());
    }
    
    /**
     * 删除任务,先关闭再删除
     * @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提供的用于调度任务的核心接口。通过TaskScheduler,你可以灵活地安排任务的执行时间,并且可以在运行时动态地创建、取消任务。

代码示例简单使用:

复制代码
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方法动态安排任务:

复制代码
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("动态任务执行:" + System.currentTimeMillis()), new Date(System.currentTimeMillis() + 5000));
    }
}

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

  • SchedulingConfig:添加执行定时任务的线程池配置类

    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();

    ???// 定时任务执行线程池核心线程数

    ???taskScheduler.setPoolSize(4);

    ???taskScheduler.setRemoveOnCancelPolicy(true);

    ???taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");

    ???return taskScheduler;

    ???}

    }

  • ScheduledTask:添加ScheduledFuture的包装类

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

复制代码
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接口实现类

添加Runnable接口实现类,被定时任务线程池调用,用来执行指定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("定时任务开始执行 - bean:{},方法:{},参数:{}", 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("定时任务执行异常 - bean:%s,方法:%s,参数:%s ", beanName, methodName, params), ex);
????????}

????????long times = System.currentTimeMillis() - startTime;
????????logger.info("定时任务执行结束 - bean:{},方法:{},参数:{},耗时:{} 毫秒", 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 findAllTask() {

    ???List 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;

    ???/**

    ???* 方法参数: 执行service里面的哪一种方法

    ???*/

    ???private String methodParams;

    ???/**

    ???* cron表达式

    ???*/

    ???private String cronExpression;

    ???/**

    ???* 状态(1正常 0暂停)

    ???*/

    ???private Integer jobStatus;

    ???/**

    ???* 备注

    ???*/

    ???private String remark;

    ???/**

    ???* 创建时间

    ???*/

    ???private String createTime;

    ???/**

    ???* 更新时间

    ???*/

    ???private String updateTime;

    }

  • ScheduleJobStatus:任务状态枚举类型

    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 getBean(Class clazz) {

    ???return getApplicationContext().getBean(clazz);

    ???}

    ???// 通过name,以及Clazz返回指定的Bean

    ???public static T getBean(String name, Class 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;

    ???}

    ???//然后判断是否开启, 如果开启的话,现在立即执行

    ???startOrStopSchedulerJob(currentSchedule, true);

    ???}

    ???public void deleteScheduleJob(ScheduleResult scheduleResult) {

    ???// 清除这个任务

    ???cronTaskRegistrar.removeCronTask(scheduleResult.getBeanName(), scheduleResult.getMethodName(), scheduleResult.getMethodParams());

    ???// 清除这个任务的数据

    ???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("停止或暂停:现在是开启");

    ???cronTaskRegistrar.addCronTask(scheduleResult);

    ???return;

    ???}

    ???System.out.println("停止或暂停:现在是暂停");

    ???if (update){

    ???cronTaskRegistrar.removeCronTask(scheduleResult);

    ???return;

    ???}

    ???cronTaskRegistrar.removeCronTask(scheduleResult.getBeanName(), scheduleResult.getMethodName(), scheduleResult.getMethodParams());

    ???}

    ???public List findAllTask() {

    ???return cronTaskRegistrar.findAllTask();

    ???}

    ???// 转换为年-月-日 时:分:秒

    ???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;

    ???/**

    ???* 测试上传的用例文件, 获取详细执行结果

    ???*/

    ???@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 get() throws Exception {

    ???List allTask = scheduleJobService.findAllTask();

    ???System.out.println("现在的定时任务数量 = " + allTask.size());

    ???System.out.println("现在的定时任务 = " + 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实现定时任务

Quartz是一个功能强大的开源任务调度框架,支持复杂的任务调度需求,如任务的持久化、分布式任务管理、基于数据库的调度等。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秒执行一次
        factoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
        return factoryBean;
    }

    public static class SampleJob implements Job {
        @Override
        public void execute(JobExecutionContext context) {
            System.out.println("Quartz任务执行:" + System.currentTimeMillis());
        }
    }
}

springboot整合

  • 数据库设计

将任务计划放入数据库中保存。在启动任务是,从数据库中查找任务计划信息,并动态配置进去即可。

复制代码
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='定时任务运行状态表';
  • 定时任务管理

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

主要包括:

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

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

详细代码如下:

复制代码
/**
?* 定时任务管理服务
?*/
@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);//获取任务执行类
????????} catch (ClassNotFoundException e) {
????????????throw new Exception("任务类不存在");
????????}
????????//创建job,指定job名称和分组
????????JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(taskCode, jobGroup).build();
????????//创建表达式工作计划
????????CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
????????//创建触发器
????????CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(taskCode, jobGroup)
????????????????.withSchedule(cronScheduleBuilder).build();
????????scheduler.scheduleJob(jobDetail, cronTrigger);
????}

????/**
?????* 修改定时任务执行时间
?????* @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("修改定时任务执行时间报错");
????????????}
????????}
????}

????/**
?????* 暂停某个定时任务(任务恢复后,暂停时间段内未执行的任务会继续执行,如暂停时间段内有2次,则会执行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就是定时任务具体要处理的系统业务逻辑,需要实现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("执行定时任务:" + 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调用了QartzService的对应接口,并做了一个写数据库读写操作,主要记录定时任务状态、执行记录信息的等。

复制代码
@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());
????????}
????}
}
相关推荐
by__csdn2 小时前
微服务与单体那些事儿
java·后端·微服务·云原生·架构
权泽谦2 小时前
Java 在机器学习中的应用:基于 DL4J 与 Weka 的完整实战案例
java·机器学习·数据挖掘
q***23922 小时前
nginx简单命令启动,关闭等
java·服务器·nginx
拾忆,想起2 小时前
Dubbo负载均衡全解析:五种策略详解与实战指南
java·运维·微服务·架构·负载均衡·dubbo·哈希算法
shayudiandian2 小时前
【Java】关键字 native
java
合作小小程序员小小店2 小时前
桌面开发,在线%幼儿教育考试管理%系统,基于eclipse,java,swing,mysql数据库
java·数据库·sql·mysql·eclipse·jdk
明洞日记3 小时前
【设计模式手册005】单例模式 - 唯一实例的优雅实现
java·单例模式·设计模式
optimistic_chen3 小时前
【Java EE进阶 --- SpringBoot】AOP原理
spring boot·笔记·后端·java-ee·开源·aop
二川bro3 小时前
第48节:WebAssembly加速与C++物理引擎编译
java·c++·wasm