• 【quartz】从数据库中读取配置实现动态定时任务


    前言

            在Java中定时任务是很常见的,使用@Scheduled注解即可实现定时,但是在生产环境中可能会遇到页面上自主配置定时任务的定时时间,这种情况下单独用@Scheduled注解很难实现定时任务,所以我们可以将定时任务存储在数据库中并通过quartz实现动态定时任务。


    配置依赖

    1. <dependency>
    2. <groupId>org.quartz-scheduler</groupId>
    3. <artifactId>quartz</artifactId>
    4. <version>2.2.1</version>
    5. </dependency>
    6. <dependency>
    7. <groupId>org.quartz-scheduler</groupId>
    8. <artifactId>quartz-jobs</artifactId>
    9. <version>2.2.1</version>
    10. </dependency>
    11. <dependency>
    12. <groupId>org.springframework</groupId>
    13. <artifactId>spring-context</artifactId>
    14. </dependency>
    15. <dependency>
    16. <groupId>org.springframework</groupId>
    17. <artifactId>spring-context-support</artifactId>
    18. </dependency>

    实现方式

    1、定时任务表实体

    1. @Data
    2. @TableName("SCHEDULE_JOB")
    3. public class ScheduleJob {
    4. /**
    5. * 序号
    6. */
    7. @TableId(value = "ID", type = IdType.ID_WORKER_STR)
    8. private String id;
    9. /**
    10. * 任务名称
    11. */
    12. @TableField("JOB_NAME")
    13. private String jobName;
    14. /**
    15. * 任务分组
    16. */
    17. @TableField("JOB_GROUP")
    18. private String jobGroup;
    19. /**
    20. * 任务状态 是否启动任务,2:失效;1:有效
    21. */
    22. @TableField("JOB_STATUS")
    23. private Integer jobStatus;
    24. /**
    25. * cron表达式,推荐使用6域的
    26. */
    27. @TableField("CRON_EXPRESSION")
    28. private String cronExpression;
    29. /**
    30. * 描述
    31. */
    32. @TableField("DESCRIPTION")
    33. private String description;
    34. /**
    35. * 任务执行时调用哪个类的方法 包名+类名,全路径
    36. */
    37. @TableField("BEAN_CLASS")
    38. private String beanClass;
    39. /**
    40. * 任务是否有状态
    41. */
    42. @TableField("IS_CONCURRENT")
    43. private Integer isConcurrent;
    44. /**
    45. * spring bean 对应定时任务的类名,首字母小写
    46. */
    47. @TableField("SPRING_ID")
    48. private String springId;
    49. /**
    50. * 任务调用的方法名
    51. */
    52. @TableField("method_name")
    53. private String methodName;
    54. @TableField("CREATE_TIME")
    55. private Date createTime;
    56. @TableField("UPDATE_TIME")
    57. private Date updateTime;
    58. }

    2、反射调用scheduleJob中定义的方法

    1. import com.saas.reptile.common.result.Result;
    2. import com.saas.reptile.entity.po.ScheduleJob;
    3. import com.saas.reptile.utils.LogUtils;
    4. import com.saas.reptile.utils.SpringUtils;
    5. import com.saas.reptile.utils.StringUtils;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import org.springframework.context.ApplicationContext;
    8. import java.lang.reflect.Method;
    9. public class TaskUtils {
    10. @Autowired
    11. private ApplicationContext applicationContext;
    12. /**
    13. * 通过反射调用scheduleJob中定义的方法
    14. *
    15. * @param scheduleJob
    16. */
    17. public static void invokMethod(ScheduleJob scheduleJob) {
    18. try {//添加最大的异常捕获
    19. String springId = scheduleJob.getSpringId();
    20. Object object = null;
    21. Class clazz = null;
    22. //根据反射来进行
    23. if (StringUtils.isNotBlank(springId)) {
    24. object = SpringUtils.getBean(springId);
    25. }
    26. if (object == null && StringUtils.isNotBlank(scheduleJob.getBeanClass())) {
    27. String jobStr = "定时任务名称 = [" + scheduleJob.getJobName() + "]-在spring 中没有这个 springId, 通过 class type 获取中...";
    28. LogUtils.info(jobStr, scheduleJob.getBeanClass());
    29. try {
    30. clazz = Class.forName(scheduleJob.getBeanClass());
    31. object = SpringUtils.getBean(clazz);
    32. if(object == null){
    33. jobStr = "定时任务名称 = [" + scheduleJob.getJobName() + "]-在spring 中没有获得 bean, 调用 spring 方法再次构建中...";
    34. LogUtils.info(jobStr, scheduleJob.getBeanClass());
    35. object = SpringUtils.getBeanByType(clazz);
    36. }
    37. if (StringUtils.isNotBlank(springId)) {
    38. SpringUtils.setBean(springId, object);
    39. LogUtils.info("spring bean 构建完成并加入到容器中 ", scheduleJob.getBeanClass());
    40. }
    41. LogUtils.info("定时任务 spring bean 构建成功! ", scheduleJob.getBeanClass());
    42. } catch (Exception e) {
    43. LogUtils.error("定时任务 spring bean 构建失败了!!! ", scheduleJob.getBeanClass(), e);
    44. Result.fail(e);
    45. return;
    46. }
    47. }
    48. clazz = object.getClass();
    49. Method method = null;
    50. try {
    51. method = clazz.getDeclaredMethod(scheduleJob.getMethodName());
    52. } catch (NoSuchMethodException e) {
    53. String jobStr = "定时任务名称 = [" + scheduleJob.getJobName() + "] = 未启动成功,方法名设置错误!!!";
    54. LogUtils.error(jobStr, e);
    55. } catch (SecurityException e) {
    56. LogUtils.error("TaskUtils发生异常", e);
    57. Result.fail(e);
    58. }
    59. if (method != null) {
    60. try {
    61. method.invoke(object);
    62. LogUtils.info("定时任务名称 = [" + scheduleJob.getJobName() + "] = 启动成功");
    63. } catch (Exception e) {
    64. Result.fail(e);
    65. LogUtils.error("定时任务名称 = [" + scheduleJob.getJobName() + "] = 启动失败了!!!", e);
    66. return;
    67. }
    68. } else {
    69. String jobStr = "定时任务名称 = [" + scheduleJob.getJobName() + "] = 启动失败了!!!";
    70. LogUtils.error(jobStr, clazz.getName(), "not find method ");
    71. }
    72. } catch (Exception e) {//添加最大的异常捕获
    73. Result.fail(e);
    74. LogUtils.error("定时任务名称 = [" + scheduleJob.getJobName() + "] = 启动失败了!!!", e);
    75. }
    76. }
    77. }

    3、创建正常状态下的job执行工厂

    1. import com.saas.reptile.entity.po.ScheduleJob;
    2. import org.quartz.Job;
    3. import org.quartz.JobExecutionContext;
    4. import org.quartz.JobExecutionException;
    5. public class QuartzJobFactory implements Job {
    6. @Override
    7. public void execute(JobExecutionContext context) throws JobExecutionException {
    8. ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");
    9. TaskUtils.invokMethod(scheduleJob);
    10. }
    11. }

    4、若一个方法一次执行不完下次轮转时则等待改方法执行完后才执行下一次操作

    1. import com.saas.reptile.entity.po.ScheduleJob;
    2. import org.quartz.DisallowConcurrentExecution;
    3. import org.quartz.Job;
    4. import org.quartz.JobExecutionContext;
    5. import org.quartz.JobExecutionException;
    6. @DisallowConcurrentExecution
    7. public class QuartzJobFactoryDisallowConcurrentExecution implements Job {
    8. @Override
    9. public void execute(JobExecutionContext context) throws JobExecutionException {
    10. ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");
    11. TaskUtils.invokMethod(scheduleJob);
    12. }
    13. }

    5、创建需要运行的定时任务类

    1. @Component
    2. public class ClassAtmosphereTask {
    3. public void work(){
    4. // do something
    5. }
    6. }

    6、SpringBean工厂工具类

    1. import org.springframework.beans.BeansException;
    2. import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    3. import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    4. import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    5. import org.springframework.stereotype.Component;
    6. @Component
    7. public class SpringUtils implements BeanFactoryPostProcessor {
    8. // Spring应用上下文环境
    9. private static ConfigurableListableBeanFactory beanFactory;
    10. @Override
    11. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    12. SpringUtils.beanFactory = beanFactory;
    13. }
    14. /**
    15. * 获取对象
    16. *
    17. * @param name
    18. * @return Object 一个以所给名字注册的bean的实例
    19. * @throws BeansException
    20. */
    21. @SuppressWarnings("unchecked")
    22. public static <T> T getBean(String name) throws BeansException {
    23. return (T) beanFactory.getBean(name);
    24. }
    25. /**
    26. * 获取对象
    27. * @return Object 一个以所给名字注册的bean的实例
    28. * @throws BeansException
    29. */
    30. @SuppressWarnings("unchecked")
    31. public static <T> T getBeanByType(Class<T> clzee) throws BeansException {
    32. try {
    33. return beanFactory.createBean(clzee);
    34. } catch (NoSuchBeanDefinitionException e) {
    35. return null;
    36. }
    37. }
    38. /**
    39. * 注入一个对象
    40. * @return Object 一个以所给名字注册的bean的实例
    41. * @throws BeansException
    42. */
    43. @SuppressWarnings("unchecked")
    44. public static void setBean(String springId, Object obj) throws BeansException {
    45. beanFactory.registerSingleton(springId, obj);
    46. }
    47. /**
    48. * 获取类型为requiredType的对象
    49. * @return
    50. * @throws BeansException
    51. */
    52. public static <T> T getBean(Class<T> clz) throws BeansException {
    53. try {
    54. @SuppressWarnings("unchecked")
    55. T result = (T) beanFactory.getBean(clz);
    56. return result;
    57. } catch (NoSuchBeanDefinitionException e) {
    58. return null;
    59. }
    60. }
    61. /**
    62. * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
    63. *
    64. * @param name
    65. * @return boolean
    66. */
    67. public static boolean containsBean(String name) {
    68. return beanFactory.containsBean(name);
    69. }
    70. /**
    71. * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。
    72. * 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
    73. *
    74. * @param name
    75. * @return boolean
    76. * @throws NoSuchBeanDefinitionException
    77. */
    78. public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
    79. return beanFactory.isSingleton(name);
    80. }
    81. /**
    82. * @param name
    83. * @return Class 注册对象的类型
    84. * @throws NoSuchBeanDefinitionException
    85. */
    86. public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
    87. return beanFactory.getType(name);
    88. }
    89. /**
    90. * 如果给定的bean名字在bean定义中有别名,则返回这些别名
    91. *
    92. * @param name
    93. * @return
    94. * @throws NoSuchBeanDefinitionException
    95. */
    96. public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
    97. return beanFactory.getAliases(name);
    98. }
    99. }

    7、其他用到的工具类

    • 字符串工具类
    1. public class StringUtils {
    2. public final static String REG_DIGIT = "[0-9]*";
    3. public final static String REG_CHAR = "[a-zA-Z]*";
    4. public final static String EMPTY = "";
    5. /**
    6. * 判断是否为空
    7. */
    8. public static boolean isEmpty(Object... obj) {
    9. if(obj == null)
    10. return true;
    11. for(Object object : obj) {
    12. if (object == null)
    13. return true;
    14. if (object.toString().trim().length() == 0)
    15. return true;
    16. }
    17. return false;
    18. }
    19. public static boolean isBlankEmpty(Object obj) {
    20. if (obj == null || "".equals(obj) || "".equals(obj.toString().trim()) || "null".equalsIgnoreCase(obj.toString()))
    21. return true;
    22. return false;
    23. }
    24. /**
    25. * 是否空,或者为空串,或者为"null"
    26. * @author guoyu
    27. */
    28. public static boolean isBlankEmpty(Object... objs) {
    29. if (objs == null || objs.length == 0)
    30. return true;
    31. for (Object obj : objs) {
    32. if (isBlankEmpty(obj)) {
    33. return true;
    34. }
    35. }
    36. return false;
    37. }
    38. public static boolean isNotBlank(String pattern) {
    39. return !isBlankEmpty(pattern);
    40. }
    41. public static boolean isBlank(String pattern) {
    42. return isBlankEmpty(pattern);
    43. }
    44. public static String formatCountNames(String nameList) {
    45. String[] names = nameList.split(",");
    46. Map<String, Integer> nameCount = new HashMap<String, Integer>();
    47. for(String name : names) {
    48. if(StringUtils.isEmpty(name)) continue;
    49. if(nameCount.containsKey(name)) {
    50. Integer count = nameCount.get(name) + 1;
    51. nameCount.put(name, count);
    52. } else {
    53. nameCount.put(name, 1);
    54. }
    55. }
    56. StringBuilder newNames = new StringBuilder();
    57. for(String key : nameCount.keySet()) {
    58. if(StringUtils.isEmpty(key)) continue;
    59. Integer count = nameCount.get(key);
    60. String splitChar = newNames.length() > 0 ? "," : "";
    61. newNames.append(splitChar).append(key).append("x").append(count);
    62. }
    63. return newNames.toString();
    64. }
    65. public static boolean isDigit(String str){
    66. return isNumeric(str);
    67. }
    68. public static boolean isChar(String str){
    69. return str.matches(REG_CHAR);
    70. }
    71. public static Boolean isNotEmpty(Object... obj) {
    72. Boolean r = StringUtils.isEmpty(obj);
    73. return !r;
    74. }
    75. public static boolean isNumeric(String str) {
    76. if(isBlankEmpty(str)) return false;
    77. for (int i = str.length(); --i >= 0;) {
    78. if (!Character.isDigit(str.charAt(i))) {
    79. return false;
    80. }
    81. }
    82. return true;
    83. }
    84. /**
    85. * string 中的 str 在倒数 num 中的位置
    86. * @author guoyu
    87. */
    88. public static int stringLastlndex(String string, String str, int num) {
    89. int indexOf = string.lastIndexOf(str);
    90. if(num > 1){
    91. return stringLastlndex(string.substring(0, indexOf), str, num - 1);
    92. } else {
    93. return indexOf;
    94. }
    95. }
    96. public static String getValue(Object val) {
    97. return val == null ? "" : val.toString().replace("\n", "");
    98. }
    99. public static String getFileName(boolean type, Date startDate, String tableName){
    100. String dateString = dateFormat(startDate,"yyyyMMdd");
    101. StringBuffer stringBuffer = new StringBuffer(dateString);
    102. stringBuffer.append("_");
    103. stringBuffer.append(tableName);
    104. stringBuffer.append("_");
    105. if (type) {
    106. stringBuffer.append("insert&");
    107. } else {
    108. stringBuffer.append("update&");
    109. }
    110. return stringBuffer.toString();
    111. }
    112. public static String getRefundNumber(Integer payRefundNum) {
    113. if (payRefundNum == null) payRefundNum = 0;
    114. payRefundNum = payRefundNum + 1;
    115. String string = String.valueOf(payRefundNum);
    116. if (string.length() == 1) {
    117. return "0"+string;
    118. }
    119. return string;
    120. }
    121. private static String dateFormat(Date date, String datePattern) {
    122. if(date == null) return "";
    123. if(datePattern == null) datePattern = "yyyy-MM-dd";
    124. SimpleDateFormat df = new SimpleDateFormat(datePattern, Locale.UK);
    125. return df.format(date);
    126. }
    127. }
    • 日志工具类
    1. import org.apache.commons.logging.LogFactory;
    2. public class LogUtils {
    3. private static final org.apache.commons.logging.Log logger;
    4. private static final Object lock = new Object();
    5. static {
    6. synchronized (lock) {
    7. logger = LogFactory.getLog(LogUtils.class);
    8. }
    9. }
    10. public static void info(Object... msgs) {
    11. StringBuilder stringBuilder = new StringBuilder();
    12. Throwable e = null;
    13. for (Object msg : msgs) {
    14. if (msg != null) {
    15. if (msg instanceof Throwable) {
    16. e = (Throwable) msg;
    17. } else {
    18. stringBuilder.append(msg).append(" ");
    19. }
    20. }
    21. }
    22. logger.info(stringBuilder, e);
    23. }
    24. public static void error(Object... msgs) {
    25. StringBuilder stringBuilder = new StringBuilder();
    26. Throwable e = null;
    27. for (Object msg : msgs) {
    28. if (msg != null) {
    29. if (msg instanceof Throwable) {
    30. e = (Throwable) msg;
    31. } else {
    32. stringBuilder.append(msg).append(" ");
    33. }
    34. }
    35. }
    36. logger.error(stringBuilder, e);
    37. }
    38. public static void warn(Object... msgs) {
    39. StringBuilder stringBuilder = new StringBuilder();
    40. Throwable e = null;
    41. for (Object msg : msgs) {
    42. if (msg != null) {
    43. if (msg instanceof Throwable) {
    44. e = (Throwable) msg;
    45. } else {
    46. stringBuilder.append(msg).append(" ");
    47. }
    48. }
    49. }
    50. logger.warn(stringBuilder, e);
    51. }
    52. public static void debug(Object... msgs) {
    53. StringBuilder stringBuilder = new StringBuilder();
    54. Throwable e = null;
    55. for (Object msg : msgs) {
    56. if (msg != null) {
    57. if (msg instanceof Throwable) {
    58. e = (Throwable) msg;
    59. } else {
    60. stringBuilder.append(msg).append(" ");
    61. }
    62. }
    63. }
    64. logger.debug(stringBuilder, e);
    65. }
    66. }
    • 数据分析常量类
    1. public final class Constant {
    2. private Constant() {
    3. }
    4. // 定时任务运行状态:运行
    5. public static final Integer STATUS_RUNNING = 1;
    6. // 定时任务运行状态:停止
    7. public static final Integer STATUS_NOT_RUNNING = 2;
    8. // 定时任务是否有状态:有(默认为1,弃用)
    9. public static final Integer CONCURRENT_IS = 1;
    10. // 定时任务是否有状态:无(默认为1,弃用)
    11. public static final Integer CONCURRENT_NOT = 0;
    12. }
    • 接口返回参数类
    1. @Data
    2. public class Result<T> {
    3. private Integer code;
    4. private T data;
    5. private String msg;
    6. public Result() {
    7. super();
    8. }
    9. public Result(T data) {
    10. this.data = data;
    11. }
    12. public static Result success()
    13. {
    14. return success("");
    15. }
    16. public static <T> Result success(T data) {
    17. Result<T> result = new Result<>(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getName());
    18. result.setData(data);
    19. return result;
    20. }
    21. public static <T> Result<T> fail(ResultEnum resultEnum) {
    22. return createResult(null, resultEnum.getCode(), resultEnum.getName());
    23. }
    24. public static <T> Result<T> fail(Integer code, String message) {
    25. return createResult(null, code, message);
    26. }
    27. public static <T> Result<T> fail(Throwable e) {
    28. //打印异常栈信息到控制台
    29. e.printStackTrace();
    30. return createResult(null, 500, "服务器发生错误");
    31. }
    32. private static <T> Result<T> createResult(T data, Integer code, String message) {
    33. Result<T> r = new Result<>();
    34. r.setCode(code);
    35. r.setData(data);
    36. r.setMsg(message);
    37. return r;
    38. }
    39. public Result(Integer code, String msg)
    40. {
    41. this.code = code;
    42. this.msg = msg;
    43. }
    44. }

    其中ResultEnum类如下:

    1. public enum ResultEnum {
    2. SUCCESS(200,"请求成功"),
    3. UN_KNOWN_ERROR(-1, "未知错误"),
    4. PARAM_NULL_ERROR(10001,"参数为空"),
    5. PERMISSION_ACCESS_DENIED(50001, "permission access denied");
    6. private int code;
    7. private String name;
    8. private ResultEnum(int code, String name) {
    9. this.code = code;
    10. this.name = name;
    11. }
    12. public int getCode() {
    13. return this.code;
    14. }
    15. public String getName() {
    16. return this.name;
    17. }
    18. public static ResultEnum getNameByCode(int code) {
    19. for (ResultEnum resultEnum : ResultEnum.values()) {
    20. if (code == resultEnum.getCode()) {
    21. return resultEnum;
    22. }
    23. }
    24. return null;
    25. }
    26. }

    8、sql语句

    1. SET NAMES utf8mb4;
    2. SET FOREIGN_KEY_CHECKS = 0;
    3. -- ----------------------------
    4. -- Table structure for schedule_job
    5. -- ----------------------------
    6. DROP TABLE IF EXISTS `schedule_job`;
    7. CREATE TABLE `schedule_job` (
    8. `id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '主键',
    9. `job_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '任务名称',
    10. `job_group` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '任务分组',
    11. `job_status` int(0) DEFAULT NULL COMMENT '任务状态 是否启动任务;1:有效;2:失效',
    12. `cron_expression` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'cron表达式,推荐使用6域的',
    13. `description` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '定时任务描述',
    14. `bean_class` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '任务执行时调用哪个类的方法 包名+类名,全路径',
    15. `is_concurrent` int(0) DEFAULT 1 COMMENT '任务是否有状态;0:没有;1:有',
    16. `spring_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '注册的Bean,所以与类名保持一致,首字母小写',
    17. `method_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '任务调用的方法名',
    18. `create_time` datetime(0) DEFAULT NULL COMMENT '创建时间',
    19. `update_time` datetime(0) DEFAULT NULL COMMENT '更新时间',
    20. PRIMARY KEY (`id`) USING BTREE
    21. ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
    22. -- ----------------------------
    23. -- Records of schedule_job
    24. -- ----------------------------
    25. INSERT INTO `schedule_job` VALUES ('1', 'ClassAtmosphereTask', 'ClassAtmosphereTask', 1, '0 30 23 * * ?', '班级氛围值定时任务', 'com.saas.reptile.task.ClassAtmosphereTask', 1, 'classAtmosphereTask', 'work', '2022-06-25 21:38:42', '2022-06-25 21:38:45');
    26. SET FOREIGN_KEY_CHECKS = 1;

    注释的很详细,其中:

    job_name和job_group保持一致即可;

    is_concurrent默认为1;

    任务执行的类要全路径等等。

    9、方法实现

    写在service的实现类里

    1. package com.saas.reptile.service.impl;
    2. import com.baomidou.mybatisplus.service.impl.ServiceImpl;
    3. import com.saas.reptile.common.quartz.QuartzJobFactory;
    4. import com.saas.reptile.common.quartz.QuartzJobFactoryDisallowConcurrentExecution;
    5. import com.saas.reptile.entity.po.ScheduleJob;
    6. import com.saas.reptile.mapper.ScheduleJobMapper;
    7. import com.saas.reptile.service.ScheduleJobService;
    8. import com.saas.reptile.utils.Constant;
    9. import com.saas.reptile.utils.LogUtils;
    10. import org.quartz.*;
    11. import org.quartz.impl.matchers.GroupMatcher;
    12. import org.springframework.beans.factory.annotation.Autowired;
    13. import org.springframework.stereotype.Service;
    14. import org.springframework.transaction.annotation.Propagation;
    15. import org.springframework.transaction.annotation.Transactional;
    16. import org.springframework.scheduling.quartz.SchedulerFactoryBean;
    17. import javax.annotation.PostConstruct;
    18. import java.util.ArrayList;
    19. import java.util.List;
    20. import java.util.Set;
    21. @Service
    22. public class ScheduleJobServiceImpl extends ServiceImpl<ScheduleJobMapper, ScheduleJob>
    23. implements ScheduleJobService {
    24. @Autowired
    25. private SchedulerFactoryBean schedulerFactoryBean;
    26. /**
    27. * 添加定时任务到数据库
    28. * @param scheduleJob
    29. */
    30. @Override
    31. @Transactional(propagation = Propagation.REQUIRED)
    32. public void addTask(ScheduleJob scheduleJob) {
    33. this.baseMapper.insert(scheduleJob);
    34. }
    35. /**
    36. * 改变定时任务状态
    37. * @param id
    38. * @param status
    39. */
    40. @Override
    41. @Transactional(propagation = Propagation.REQUIRED)
    42. public void changeJobStatus(String id, Integer status) throws SchedulerException {
    43. // EntityWrapper<ScheduleJob> wrapper = new EntityWrapper<>();
    44. // wrapper.eq("id",id);
    45. // String setSql = "job_status = " + status;
    46. // this.baseMapper.updateForSet(setSql, wrapper);
    47. ScheduleJob job = selectById(id);
    48. if (status == 1){
    49. // 启动
    50. job.setJobStatus(status);
    51. addJob(job);
    52. } else if (status == 2) {
    53. // 停止
    54. deleteJob(job);
    55. job.setJobStatus(status);
    56. }
    57. // 更新定时任务
    58. updateById(job);
    59. }
    60. /**
    61. * 更新定时任务cron表达式与运行状态
    62. * @param jobId 定时任务主键ID
    63. * @param cron 表达式
    64. * @param status 定时状态
    65. */
    66. @Override
    67. @Transactional(propagation = Propagation.REQUIRED)
    68. public void updateCronStatus(String jobId, String cron, Integer status) throws SchedulerException {
    69. ScheduleJob job = selectById(jobId);
    70. if (status == 1){
    71. // 启动
    72. job.setJobStatus(status);
    73. job.setCronExpression(cron);
    74. addJob(job);
    75. } else if (status == 2) {
    76. // 停止
    77. deleteJob(job);
    78. job.setJobStatus(status);
    79. job.setCronExpression(cron);
    80. }
    81. // 更新定时任务
    82. updateById(job);
    83. }
    84. /**
    85. * 添加任务
    86. *
    87. * @param job
    88. * @throws SchedulerException
    89. */
    90. @Transactional(propagation = Propagation.REQUIRED)
    91. public void addJob(ScheduleJob job) throws SchedulerException {
    92. // 定时任务运行状态为1则进入下一步
    93. if (job == null || !Constant.STATUS_RUNNING.equals(job.getJobStatus())) {
    94. return;
    95. }
    96. Scheduler scheduler = schedulerFactoryBean.getScheduler();
    97. LogUtils.info(scheduler + ".......................................................................................add");
    98. TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
    99. CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
    100. // 不存在,创建一个
    101. if (null == trigger) {
    102. Class clazz = Constant.CONCURRENT_IS.equals(job.getIsConcurrent()) ? QuartzJobFactory.class : QuartzJobFactoryDisallowConcurrentExecution.class;
    103. JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();
    104. jobDetail.getJobDataMap().put("scheduleJob", job);
    105. CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
    106. trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(scheduleBuilder).build();
    107. scheduler.scheduleJob(jobDetail, trigger);
    108. } else {
    109. // Trigger已存在,那么更新相应的定时设置
    110. CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
    111. // 按新的cronExpression表达式重新构建trigger
    112. trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
    113. // 按新的trigger重新设置job执行
    114. scheduler.rescheduleJob(triggerKey, trigger);
    115. }
    116. }
    117. @PostConstruct
    118. public void init() throws Exception {
    119. LogUtils.info("实例化List<ScheduleJob>,从数据库读取....",this);
    120. // 这里获取任务信息数据
    121. List<ScheduleJob> jobList = selectList(null);
    122. for (ScheduleJob job : jobList) {
    123. addJob(job);
    124. }
    125. }
    126. /**
    127. * 获取所有计划中的任务列表
    128. *
    129. * @return
    130. * @throws SchedulerException
    131. */
    132. public List<ScheduleJob> getAllJob() throws SchedulerException {
    133. Scheduler scheduler = schedulerFactoryBean.getScheduler();
    134. GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
    135. Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
    136. List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
    137. for (JobKey jobKey : jobKeys) {
    138. List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
    139. for (Trigger trigger : triggers) {
    140. ScheduleJob job = new ScheduleJob();
    141. job.setJobName(jobKey.getName());
    142. job.setJobGroup(jobKey.getGroup());
    143. job.setDescription("触发器:" + trigger.getKey());
    144. Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
    145. job.setJobStatus(Integer.parseInt(triggerState.name()));
    146. if (trigger instanceof CronTrigger) {
    147. CronTrigger cronTrigger = (CronTrigger) trigger;
    148. String cronExpression = cronTrigger.getCronExpression();
    149. job.setCronExpression(cronExpression);
    150. }
    151. jobList.add(job);
    152. }
    153. }
    154. return jobList;
    155. }
    156. /**
    157. * 所有正在运行的job
    158. *
    159. * @return
    160. * @throws SchedulerException
    161. */
    162. public List<ScheduleJob> getRunningJob() throws SchedulerException {
    163. Scheduler scheduler = schedulerFactoryBean.getScheduler();
    164. List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
    165. List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
    166. for (JobExecutionContext executingJob : executingJobs) {
    167. ScheduleJob job = new ScheduleJob();
    168. JobDetail jobDetail = executingJob.getJobDetail();
    169. JobKey jobKey = jobDetail.getKey();
    170. Trigger trigger = executingJob.getTrigger();
    171. job.setJobName(jobKey.getName());
    172. job.setJobGroup(jobKey.getGroup());
    173. job.setDescription("触发器:" + trigger.getKey());
    174. Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
    175. job.setJobStatus(Integer.parseInt(triggerState.name()));
    176. if (trigger instanceof CronTrigger) {
    177. CronTrigger cronTrigger = (CronTrigger) trigger;
    178. String cronExpression = cronTrigger.getCronExpression();
    179. job.setCronExpression(cronExpression);
    180. }
    181. jobList.add(job);
    182. }
    183. return jobList;
    184. }
    185. /**
    186. * 暂停一个job
    187. *
    188. * @param scheduleJob
    189. * @throws SchedulerException
    190. */
    191. public void pauseJob(ScheduleJob scheduleJob) throws SchedulerException {
    192. Scheduler scheduler = schedulerFactoryBean.getScheduler();
    193. JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    194. scheduler.pauseJob(jobKey);
    195. }
    196. /**
    197. * 恢复一个job
    198. *
    199. * @param scheduleJob
    200. * @throws SchedulerException
    201. */
    202. public void resumeJob(ScheduleJob scheduleJob) throws SchedulerException {
    203. Scheduler scheduler = schedulerFactoryBean.getScheduler();
    204. JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    205. scheduler.resumeJob(jobKey);
    206. }
    207. /**
    208. * 删除一个job
    209. *
    210. * @param scheduleJob
    211. * @throws SchedulerException
    212. */
    213. public void deleteJob(ScheduleJob scheduleJob) throws SchedulerException {
    214. Scheduler scheduler = schedulerFactoryBean.getScheduler();
    215. JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    216. scheduler.deleteJob(jobKey);
    217. }
    218. /**
    219. * 立即执行job
    220. *
    221. * @param scheduleJob
    222. * @throws SchedulerException
    223. */
    224. public void runAJobNow(ScheduleJob scheduleJob) throws SchedulerException {
    225. Scheduler scheduler = schedulerFactoryBean.getScheduler();
    226. JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    227. scheduler.triggerJob(jobKey);
    228. }
    229. /**
    230. * 更新job时间表达式
    231. *
    232. * @param scheduleJob
    233. * @throws SchedulerException
    234. */
    235. public void updateJobCron(ScheduleJob scheduleJob) throws SchedulerException {
    236. Scheduler scheduler = schedulerFactoryBean.getScheduler();
    237. TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    238. CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
    239. CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
    240. trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
    241. scheduler.rescheduleJob(triggerKey, trigger);
    242. }
    243. }

    1、项目启动后首先运行上述方法里的init()方法,查询数据库表里的所有定时任务;

    2、然后到添加job方法【addJob】,将状态为1的定时任务推到Scheduler计划中

    3、还提供了获取所有计划中的任务列表、查询正在运行的job、暂停一个job等方法,不过暂时我没有用到。

    4、本项目是通过mybatis-plus进行数据库操作,当更新运行状态时,如果状态为1则启动job,如果状态为2则停止job。

    5、控制层方法省略,按实际生产环境来。

    参考资料:https://gitee.com/gangye/springboot_quartz_schedule

  • 相关阅读:
    vue中的生命周期有什么,怎么用
    Ubuntu20.04环境下编译MNN
    使用Django如何才能在浏览器页面中展示从摄像头中获取的画面opencv处理过的效果
    使用免费开源的Odoo CRM如何有效的获取潜在客户线索的经验分享
    Ajax,json
    机器学习笔记之高斯分布(五)推断任务之边缘概率分布与条件概率分布
    Go语言 和 Java语言对比理解系列一:函数参数传递
    #gStore-weekly | SPARQL 执行过程中高级查询函数的求值(上)
    【保姆级·创建对象】如何通过Supplier创建对象
    OpenCV的二值化处理函数threshold()详解
  • 原文地址:https://blog.csdn.net/qq_37634156/article/details/125465298