java定时任务 springboot定时任务 quartz

一、简单定时任务

主要靠注解:@Scheduled和@EnableScheduling

ps:corn表达式生成地址

1.spring boot的启动类

启动类上上加@EnableScheduling注解,允许支持@Scheduled:

2.任务类

创建一个任务类,有这个类(需要@Component注入),再带上@Schedule注解,项目一运行,就会自动跑了

@Component 
public class ScheduleTask {
    
         
    *// 每隔5秒执行一次*    
   @Scheduled(cron = "0/5 * * * * ?")    
   public void printSay() {
    
            
   		System.out.println("每隔5秒执行一次:" + new Date());    
   }     
} 

二、定时任务持久化,项目启动开始执行,使用Quartz(就是存到数据库里)

  • 一知半解,能用就行系列。。。。
  • 所有类的源码都在下边。
  • 包可以下载!包括sql,下载地址:
    https://download.csdn.net/download/weixin_43329956/25930796

1.依赖(子工程加即可)

        <!--定时器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

  • 需要两个表: t_schedule_job,t_job_log

2.t_schedule_job和t_job_log的增删改查(省略)

需要用mybatisplus生成

3.需要的工具类

3.1 SpringContextUtils

package com.mods.browser.quartz;

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 {
    
    
    public 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(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);
    }
}

3.2 ScheduleUtils

package com.mods.browser.quartz;

import com.mods.common.exception.CommonException;
import com.mods.mbg.model.ScheduleJob;
import org.quartz.*;
/**
 * 定时任务操作工具类
 */
public class ScheduleUtils {
    
    
    private final static String JOB_NAME = "TASK_";
    /**
     * 获取触发器key
     */
    public static TriggerKey getTriggerKey(Integer jobId) {
    
    
        return TriggerKey.triggerKey(JOB_NAME + jobId);
    }
    /**
     * 获取jobKey
     */
    public static JobKey getJobKey(Integer jobId) {
    
    
        return JobKey.jobKey(JOB_NAME + jobId);
    }
    /**
     * 获取表达式触发器
     */
    public static CronTrigger getCronTrigger(Scheduler scheduler, Integer jobId) {
    
    
        try {
    
    
            return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
        } catch (SchedulerException e) {
    
    
            throw new CommonException("获取定时任务CronTrigger出现异常", e);
        }
    }
    /**
     * 创建定时任务
     */
    public static Integer createScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) {
    
    
        Integer jobId = scheduleJob.getId();
        try {
    
    
            //构建job信息
            JobDetail jobDetail = JobBuilder.newJob(ScheduleJobUtils.class).withIdentity(getJobKey(jobId)).build();
            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();
            //按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getId())).withSchedule(scheduleBuilder).build();
            //放入参数,运行时的方法可以获取
            jobDetail.getJobDataMap().put(JobConstant.JOB_PARAM_KEY, scheduleJob);
            scheduler.scheduleJob(jobDetail, trigger);
            //如果任务为暂停,则将容器里的任务状态改为暂停
            if (scheduleJob.getStatus().equals("0")) {
    
    
                pauseJob(scheduler, scheduleJob.getId());
            }
            return 1;
        } catch (SchedulerException e) {
    
    
            return -1;
        }
    }
    /**
     * 更新定时任务
     */
    public static void updateScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) {
    
    
        try {
    
    
            TriggerKey triggerKey = getTriggerKey(scheduleJob.getId());
            //表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();
            CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getId());
            //按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            //参数
            trigger.getJobDataMap().put(JobConstant.JOB_PARAM_KEY, scheduleJob);
            scheduler.rescheduleJob(triggerKey, trigger);
            //暂停任务
            if (scheduleJob.getStatus().equals("0")) {
    
    
                pauseJob(scheduler, scheduleJob.getId());
            }
        } catch (SchedulerException e) {
    
    
            throw new CommonException("更新定时任务失败", e);
        }
    }
    /**
     * 立即执行
     */
    public static void run(Scheduler scheduler, ScheduleJob scheduleJob) {
    
    
        try {
    
    
            //参数
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(JobConstant.JOB_PARAM_KEY, scheduleJob);
            Integer jobId = scheduleJob.getId();
            JobKey jobKey = getJobKey(jobId);
            scheduler.triggerJob(jobKey, dataMap);
        } catch (SchedulerException e) {
    
    
            throw new CommonException("立即执行定时任务失败", e);
        }
    }
    /**
     * 暂停运行
     */
    public static void pauseJob(Scheduler scheduler, Integer jobId) {
    
    
        try {
    
    
            scheduler.pauseJob(getJobKey(jobId));
        } catch (SchedulerException e) {
    
    
            throw new CommonException("暂停定时任务失败", e);
        }
    }
    /**
     * 恢复运行
     */
    public static void resumeJob(Scheduler scheduler, Integer jobId) {
    
    
        try {
    
    
            scheduler.resumeJob(getJobKey(jobId));
        } catch (SchedulerException e) {
    
    
            throw new CommonException("暂停定时任务失败", e);
        }
    }
    /**
     * 删除定时任务
     */
    public static void deleteScheduleJob(Scheduler scheduler, Integer jobId) {
    
    
        try {
    
    
            scheduler.deleteJob(getJobKey(jobId));
        } catch (SchedulerException e) {
    
    
            throw new CommonException("删除定时任务失败", e);
        }
    }
}

3.3 ScheduleRunnable

package com.mods.browser.quartz;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;

@Slf4j
public class ScheduleRunnable implements Runnable {
    
    
    private Object target;
    private Method method;
    private String params;

    public ScheduleRunnable(String beanName, String methodName, String params) throws NoSuchMethodException, SecurityException {
    
    
        this.target = SpringContextUtils.getBean(beanName);
        this.params = params;

        if (StringUtils.isNotBlank(params)) {
    
    
            this.method = target.getClass().getDeclaredMethod(methodName, String.class);
        } else {
    
    
            this.method = target.getClass().getDeclaredMethod(methodName);
        }
    }
    
    @Override
    public void run() {
    
    
        try {
    
    
            ReflectionUtils.makeAccessible(method);
            if (StringUtils.isNotBlank(params)) {
    
    
                method.invoke(target, params);
            } else {
    
    
                method.invoke(target);
            }
        } catch (Exception e) {
    
    
            log.info("执行定时任务失败", e);
        }
    }
}

3.4 ScheduleJobUtils

package com.mods.browser.quartz;

import com.mods.mbg.mapper.JobLogMapper;
import com.mods.mbg.mapper.ScheduleJobMapper;
import com.mods.mbg.model.JobLog;
import com.mods.mbg.model.ScheduleJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 定时任务执行工具类
 */
@Slf4j
@Component
public class ScheduleJobUtils extends QuartzJobBean {
    
    

    private ExecutorService service = Executors.newSingleThreadExecutor();

    @Autowired
    private ScheduleJobMapper scheduleJobMapper;

    @Autowired
    private JobLogMapper jobLogMapper;

    /**
     * 执行任务
     *
     * @param context
     * @throws JobExecutionException
     */
    @Override
    protected void executeInternal(JobExecutionContext context) {
    
    
        ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get(JobConstant.JOB_PARAM_KEY);
        String status = scheduleJob.getStatus();
        if (!status.equals("1")) {
    
    //任务状态非执行中的
            return;
        }
        String beanName = scheduleJob.getBeanName();
        String methodName = scheduleJob.getMethodName();
        String params = scheduleJob.getParams();
        Integer jobId = scheduleJob.getId();
        String circulationStatus = scheduleJob.getCirculationStatus();
        //存入任务日志
        JobLog jobLog = new JobLog();
        jobLog.setCreateTime(new Date());
        jobLog.setJobId(jobId);
        //任务开始时间
        long startTime = System.currentTimeMillis();
        try {
    
    
            //执行任务
            log.info("id={}的任务准备执行", jobId);
            ScheduleRunnable task = new ScheduleRunnable(beanName, methodName, params);
            Future<?> future = service.submit(task);
            future.get();
            //任务执行总时长
            long times = System.currentTimeMillis() - startTime;
            jobLog.setStatus("0");
            jobLog.setTimes((int) times);
            //如果不是循环定时任务,执行成功后就修改状态为:已结束
            if ("0".equals(circulationStatus)) {
    
    
                ScheduleJob job = new ScheduleJob();
                job.setId(jobId);
                job.setStatus("2");
                scheduleJobMapper.updateById(job);
            }
            log.info("id={}的任务执行完毕,总共耗时:{}毫秒", jobId, times);
        } catch (Exception e) {
    
    
            log.error("id={}的任务执行失败,异常:{}", jobId, e);
            //任务执行总时长
            long times = System.currentTimeMillis() - startTime;
            jobLog.setStatus("1");
            jobLog.setTimes((int) times);
            jobLog.setErrorReport(StringUtils.substring(e.toString(), 0, 2000));
        } finally {
    
    
            jobLogMapper.insert(jobLog);
        }
    }
}

3.5 ScheduleJobServiceImpl

package com.mods.browser.quartz;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mods.browser.vo.ScheduleJobAddParam;
import com.mods.mbg.mapper.ScheduleJobMapper;
import com.mods.mbg.model.ScheduleJob;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.quartz.CronTrigger;
import org.quartz.Scheduler;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author leaves
 * @since 2021-09-29
 */
@Service("scheduleJobService")
public class ScheduleJobServiceImpl extends ServiceImpl<ScheduleJobMapper, ScheduleJob> implements IScheduleJobService {
    
    

    @Autowired
    private Scheduler scheduler;

    @Autowired
    private ScheduleJobMapper scheduleJobMapper;

    /**
     * 项目启动时,初始化定时器
     */
    @PostConstruct
    public void init() {
    
    
        //只查询状态正常的任务
        QueryWrapper<ScheduleJob> wrapper = new QueryWrapper<>();
        wrapper.ne("status", "2")//已完成的不进行初始化
                .eq("del_status", "0");
        List<ScheduleJob> scheduleJobList = scheduleJobMapper.selectList(wrapper);
        for (ScheduleJob scheduleJob : scheduleJobList) {
    
    
            String circulationStatus = scheduleJob.getCirculationStatus();
            String cron = scheduleJob.getCronExpression();
            if (StringUtils.isAnyBlank(circulationStatus, cron)) {
    
    
                continue;
            }
            CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getId());
            //如果不存在,则创建
            if (cronTrigger == null) {
    
    
                Integer count = ScheduleUtils.createScheduleJob(scheduler, scheduleJob);
                //如果任务创建失败则将状态改为暂停(一般任务的执行时间已经过期,任务初始化创建就会失败)
                if (count < 0) {
    
    
                    scheduleJob.setStatus("0");
                    scheduleJobMapper.updateById(scheduleJob);
                }
            } else {
    
    
                ScheduleUtils.updateScheduleJob(scheduler, scheduleJob);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer delete(Integer jobId) {
    
    
        ScheduleUtils.deleteScheduleJob(scheduler, jobId);
        Integer count = scheduleJobMapper.deleteById(jobId);
        return count;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer add(ScheduleJobAddParam param) {
    
    
        Date now = DateUtils.round(new Date(), Calendar.SECOND);
        ScheduleJob bean = new ScheduleJob();
        BeanUtils.copyProperties(param, bean);
        bean.setDelStatus("0");
        bean.setStatus("1");
        bean.setCreateTime(now);
        Integer count = this.scheduleJobMapper.insert(bean);
        if (count > 0) {
    
    
            ScheduleUtils.createScheduleJob(scheduler, bean);
            return bean.getId();
        }
        return -1;
    }

    @Override
    public Integer run(Integer jobId) {
    
    
        ScheduleJob scheduleJob = scheduleJobMapper.selectById(jobId);
        ScheduleUtils.run(scheduler, scheduleJob);
        String circulationStatus = scheduleJob.getCirculationStatus();
        Integer count = 0;
        //如果当前任务是非循环任务,立即执行后则将状态改为2——已结束
        if ("0".equals(circulationStatus)) {
    
    
            count = updateStatus(jobId, "2");//2:已结束
        }
        return count;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer pause(Integer jobId) {
    
    
        ScheduleUtils.pauseJob(scheduler, jobId);
        Integer count = updateStatus(jobId, "0");//0:停止
        return count;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer resume(Integer jobId) {
    
    
        ScheduleUtils.resumeJob(scheduler, jobId);
        Integer count = updateStatus(jobId, "1");//1:执行
        return count;
    }

    public Integer updateStatus(Integer jobId, String status) {
    
    
        ScheduleJob job = new ScheduleJob();
        job.setId(jobId);
        job.setStatus(status);
        Integer count = scheduleJobMapper.updateById(job);
        if (count > 0) {
    
    
            ScheduleJob scheduleJob = scheduleJobMapper.selectById(jobId);
            ScheduleUtils.updateScheduleJob(scheduler, scheduleJob);
        }
        return count;
    }
}

3.6 JobConstant 一个常量类

package com.mods.browser.quartz;

/**
 * 定时任务常量
 */
public class JobConstant {
    
    
    /**
     * 任务调度参数key
     */
    public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";

}

3.7 IScheduleJobService

package com.mods.browser.quartz;

import com.baomidou.mybatisplus.extension.service.IService;
import com.mods.browser.vo.ScheduleJobAddParam;
import com.mods.mbg.model.ScheduleJob;

/**
 * <p>
 * 服务类
 * </p>
 *
 * @author leaves
 * @since 2021-09-29
 */
public interface IScheduleJobService extends IService<ScheduleJob> {
    
    
    /**
     * 保存定时任务
     */
    Integer add(ScheduleJobAddParam param);

    /**
     * 删除定时任务
     */
    Integer delete(Integer jobId);

    /**
     * 立即执行
     */
    Integer run(Integer jobId);

    /**
     * 暂停运行
     */
    Integer pause(Integer jobId);

    /**
     * 恢复运行
     */
    Integer resume(Integer jobId);

}

3.8 ExecuteTask

package com.mods.browser.quartz;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 执行任务类
 */
@Component("ExecuteTask")
@Slf4j
public class ExecuteTask {
    
    

    public void test() {
    
    
        log.info("测试,定时任务开始执行----------");
    }
}

3.9 ScheduleJobController

最后controller添加定时任务

package com.mods.browser.controller;


import com.mods.browser.annotation.Log;
import com.mods.browser.quartz.IScheduleJobService;
import com.mods.browser.vo.ScheduleJobAddParam;
import com.mods.common.result.Result;
import com.mods.common.result.ResultCode;
import com.mods.mbg.model.ScheduleJob;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author leaves
 * @since 2021-09-29
 */
@RestController
@RequestMapping("/schedule-job")
@Api(tags = "ScheduleJobController", description = "定时任务管理")
public class ScheduleJobController {
    
    

    @Autowired
    private IScheduleJobService scheduleJobService;

    @ApiOperation("新增定时任务")
    @PostMapping("/add")
    @Log(module = "定时任务管理", type = "post", description = "新增定时任务")
    public Result add(@ModelAttribute @Valid ScheduleJobAddParam param) {
    
    
        Integer count = scheduleJobService.add(param);
        if (count > 0) {
    
    
            return new Result(count);
        }
        return new Result(ResultCode.FAILED);
    }

}

3.10 CommonException 抛出异常类

package com.mods.common.exception;

/**
 * 自定义异常
 *
 */
public class CommonException extends RuntimeException {
    
    
	private static final long serialVersionUID = 1L;
    private String msg;
    private Integer code=500;
    public CommonException(String msg) {
    
    
		super(msg);
		this.msg = msg;
	}
	public CommonException(String msg, Throwable e) {
    
    
		super(msg, e);
		this.msg = msg;
	}
	public CommonException(String msg, Integer code) {
    
    
		super(msg);
		this.msg = msg;
		this.code = code;
	}
	public CommonException(String msg, Integer code, Throwable e) {
    
    
		super(msg, e);
		this.msg = msg;
		this.code = code;
	}
	public String getMsg() {
    
    
		return msg;
	}
	public void setMsg(String msg) {
    
    
		this.msg = msg;
	}
	public Integer getCode() {
    
    
		return code;
	}
	public void setCode(Integer code) {
    
    
		this.code = code;
	}
}

3.11 t_schedule_job.sql

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_schedule_job
-- ----------------------------
DROP TABLE IF EXISTS `t_schedule_job`;
CREATE TABLE `t_schedule_job`  (
  `id` int(32) NOT NULL AUTO_INCREMENT,
  `bean_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '执行任务的类名',
  `method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '执行任务的方法名',
  `params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '参数(数组,“;”隔开)',
  `cron_expression` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'cron表达式',
  `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '状态(0-暂停(报错就暂停),1-执行中,2-已结束)',
  `circulation_status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否循环执行(0-否,1-是)',
  `remark` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
  `create_time` datetime(0) NULL DEFAULT NULL,
  `update_time` datetime(0) NULL DEFAULT NULL,
  `del_status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否删除(0-否,1-是)',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

3.12 t_job_log.sql

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_job_log
-- ----------------------------
DROP TABLE IF EXISTS `t_job_log`;
CREATE TABLE `t_job_log`  (
  `id` int(32) NOT NULL AUTO_INCREMENT,
  `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否报错(0-否,1-是)',
  `error_report` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '错误报告',
  `times` int(11) NULL DEFAULT NULL COMMENT '耗时(单位:毫秒)',
  `create_time` datetime(0) NULL DEFAULT NULL,
  `remark` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
  `job_id` int(32) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

猜你喜欢

转载自blog.csdn.net/weixin_43329956/article/details/120549679