Spring-Task定时任务, (springboot项目, 动态设置时间) -- Demo

SpringBoot + SpringTask + SpringDataJPA

业务:

用户选择 自动关闭 后, 可以选择一个时间(格式为yyyy-MM-dd hh:mm),时间到后触发定时任务,关闭或开启系统(修改状态)
精确到秒倒计时会出错(未知), 所以就规定用户不能选秒, 业务逻辑也是可以的(不太好).

核心代码:


DynamicTaskController :


/**
 * 定时任务接口
 *
 * @author wqkeep
 * @date 2020/6/10 14:51
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/XXXXXX/task")
public class DynamicTaskController extends BaseController {

      //BaseController 主要用于调用JSONResponse ,给JSONResponse 中的参数赋值,this.success(Object data)就是这样
      //JSONResponse 封装好的返回结果,有 是否成功success,数据data,消息提示msg,状态码status

    private final TaskServiceImpl taskServiceImpl;

    /**
     * 开启报名通道定时任务
     *
     * @param id 系统id
     * @param sysStatus 系统状态
     * @param countDownTime 倒计时时间
     * @return JSONResponse 封装好的返回结果,有 是否成功success,数据data,消息提示msg,状态码status
     * @throws OperationException 封装好的自定义异常信息
     */
    @PostMapping("/XXXXXX/startCron")
    public JSONResponse startCron(Long id, Integer sysStatus, @RequestParam("date") String countDownTime) throws OperationException {
        taskServiceImpl.startCron(id, sysStatus,countDownTime);
        return this.success(SysConst.SUCCESS);
    }

    /**
     * 关闭当前线程定时任务
     *
     * @return JSONResponse
     */
    @PostMapping("/XXXXXX/stopCron")
    public JSONResponse stopCron() {
        taskServiceImpl.stopCron();
        return this.success(SysConst.SUCCESS);
    }

    /**
     * TODO 定时任务存在问题
     *
     * 目前可以实现倒计时的功能, 但系统如果要集群就会存在问题(关闭不了当前线程, 用户可能会修改多个不同线程的定时任务).
     *
     */

}

TaskServiceImpl:


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ScheduledFuture;

/**
 * 定时任务
 *
 * @author wqkeep
 * @date 2020/6/10 17:31
 */
@Slf4j
@Service
@Transactional(rollbackOn = RuntimeException.class)
public class TaskServiceImpl implements TaskService {

    @Autowired
    private TaskRepository taskRepository; //(PO)实体类Task 对应的Repository

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    private ScheduledFuture<?> future;

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        return new ThreadPoolTaskScheduler();
    }

    /**
     * 开启报名通道定时任务
     *
     * @param id 系统id
     * @param sysStatus 系统状态
     * @param countDownTime 倒计时时间
     * @throws OperationException 自定义异常信息
     */
    public void startCron(Long id, Integer sysStatus, String countDownTime) throws OperationException {
        String currentThread = Thread.currentThread().getName();
        log.info("DynamicTask 报名通道定时任务开启: 当前线程 " + currentThread);
        String strDateFormat = "yyyy-MM-dd HH:mm";
        SimpleDateFormat sd = new SimpleDateFormat(strDateFormat);
        Date countDownDate = null;
        try {
            countDownDate = sd.parse(countDownTime);
        } catch (Exception e) {
            throw new OperationException("字符串转换日期失败");
        }
        String cron = CronDateUtils.getCron(countDownDate);
      //task为自定义的定时任务实体类 我这里主要存储cron表达式和定时任务具体时间,需要定时任务的系统名字,只有一个定时任务需求,所以写死了(不好)
        Task task = new Task();
        task.setId(1L);
        task.setCron(cron);
        task.setName("XXXXX系统");
        task.setRemark("自动开启关闭");
        task.setCountDownTime(countDownDate);
        //保存 自定义的定时任务实体类task
        taskRepository.save(task);
        if (future != null) {
            future.cancel(true);
        }
        future = threadPoolTaskScheduler.schedule(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                
                //业务代码, 我这里是修改系统状态为关闭或开启

                stopCron();
                log.info("DynamicTask 报名通道定时任务停止: 当前线程 " + currentThread);
            }
        }, new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                return new CronTrigger(cron).nextExecutionTime(triggerContext);
            }
        });
    }

    /**
     * 关闭当前线程定时任务
     */
    public void stopCron() {
        if (future != null) {
            future.cancel(true);
        }
    }

}


cron表达式和日期转换工具类CronDateUtils :

import com.zjxf.config.exception.custom.OperationException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 定时任务cron表达式与Date之间的转换
 *
 * @author wqkeep
 * @date 2020/6/10 14:51
 */
public class CronDateUtils {

     //自定义cron表达式格式 可以修改
    private static final String CRON_DATE_FORMAT = "0/1 mm HH dd MM ?";
 
    /***
     * Date转换为cron表达式
     *
     * @param date 时间
     * @return cron类型的日期
     */
    public static String getCron(final Date date){
        SimpleDateFormat sdf = new SimpleDateFormat(CRON_DATE_FORMAT);
        String formatTimeStr = "";
        if (date != null) {
            formatTimeStr = sdf.format(date);
        }
        return formatTimeStr;
    }

    /**
     * cron表达式转换为Date
     *
     * @param cron Quartz cron的类型的日期
     * @return Date日期
     * @throws OperationException 自定义异常信息
     */
    public static Date getDate(final String cron) throws OperationException {
        if(cron == null) {
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat(CRON_DATE_FORMAT);
        Date date = null;
        try {
            date = sdf.parse(cron);
        } catch (Exception e) {
            throw new OperationException("转换失败");
        }
        return date;
    }
}


参考博客:

https://blog.csdn.net/weixin_37591536/article/details/82150933

原文地址:https://www.cnblogs.com/wqkeep/p/13131432.html