【二】Quartz定时任务持久化到数据库中

Quartz定时任务持久化到数据库中

一、简介

Quartz定时任务是一个可以使用可视化界面进行控制的一个,为了更好对定时任务的管控,以及可以随时的启动、新增、修改、停止所需要的定时任务,因此需要将Quartz定时任务持久化到数据库。

二、持久化前准备

2.1、首先在数据库中创建表结构

Quartz的基本数据库表结构在官网中已经提供了对应的文件,详细如下:
Quartz官网
现在最新的稳定版本包
下载最新的稳定版本包
找到对应的数据库生成表的脚本文件
Path: quartz-2.3.0-SNAPSHOT\src\org\quartz\impl\jdbcjobstore
MySQL脚本文件
运行后生成的表信息
Quartz生成的MySQL表信息

2.2、定时任务额外的表结构

生成任务记录表

CREATE TABLE `sys_schedule_trigger` (
  `id` int NOT NULL AUTO_INCREMENT,
  `job_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务名称',
  `job_group` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务分组名称',
  `job_class` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务类:填写Job类的完整类名',
  `job_method` varchar(255) DEFAULT NULL COMMENT '任务类:填写的调用类中的方法名',
  `job_params` varchar(255) DEFAULT NULL COMMENT '任务类中的参数',
  `job_desc` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '任务描述',
  `cron` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'Cron表达式',
  `trigger_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器名称',
  `trigger_group` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器分组名称',
  `trigger_desc` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '触发器描述',
  `concurrent` int NOT NULL COMMENT '是否并发执行(0允许 1禁止)',
  `status` int NOT NULL COMMENT '状态:0禁用 1启用',
  `account` int NOT NULL COMMENT '添加人:这里需要与用户表建立主外键关系',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `job_name` (`job_name`),
  UNIQUE KEY `trigger_name` (`trigger_name`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3 COMMENT='任务记录表';

INSERT INTO `myb_blog`.`sys_schedule_trigger`(`id`, `job_name`, `job_group`, `job_class`, `job_method`, `job_params`, `job_desc`, `cron`, `trigger_name`, `trigger_group`, `trigger_desc`, `concurrent`, `status`, `account`, `create_time`) VALUES (1, '测试1', '测试组', 'com.mengyb.myb.quartz.SendClass', 'send1', NULL, '中秋节发个IMISSYOU', '0/13 * * * * ?', '测试触发器', 'trigger1', '测试1', 0, 1, 1, '2022-12-29 10:02:49');

commit;

【注】:根据个人情况创建自我需要的表结构(不唯一)

2.3、创建SpringBoot项目,生成对应的Controller、Service、Dao、Mapper代码

2.3.1、生成的表信息 sys_schedule_trigger 的实例
package com.mengyb.myb.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 * 任务记录表
 * </p>
 *
 * @author mengyb
 * @since 2022-12-29
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="SysScheduleTrigger对象", description="任务记录表")
public class SysScheduleTrigger implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    private String jobName;

    private String jobGroup;

    private String jobClass;
    private String jobMethod;
    private String jobParams;

    private String jobDesc;

    private String cron;

    private String triggerName;

    private String triggerGroup;

    private String triggerDesc;
    private Integer concurrent;
    private Integer status;

    private Integer account;

    private LocalDateTime createTime;


}

2.3.2、Mapper.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mengyb.myb.mapper.SysScheduleTriggerMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.mengyb.myb.pojo.SysScheduleTrigger">
        <id column="id" property="id" />
        <result column="job_name" property="jobName" />
        <result column="job_group" property="jobGroup" />
        <result column="job_class" property="jobClass" />
        <result column="job_method" property="jobMethod" />
        <result column="job_params" property="jobParams" />
        <result column="job_desc" property="jobDesc" />
        <result column="cron" property="cron" />
        <result column="trigger_name" property="triggerName" />
        <result column="trigger_group" property="triggerGroup" />
        <result column="trigger_desc" property="triggerDesc" />
        <result column="concurrent" property="concurrent" />
        <result column="status" property="status" />
        <result column="account" property="account" />
        <result column="create_time" property="createTime" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, job_name, job_group, job_class, job_method, job_params, job_desc, cron, trigger_name, trigger_group, trigger_desc, concurrent, status, account, create_time
    </sql>

</mapper>

2.3.3、Service层接口
package com.mengyb.myb.service;

import com.mengyb.myb.pojo.ResultBean;
import com.mengyb.myb.pojo.SysScheduleTrigger;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 * 任务记录表 服务类
 * </p>
 *
 * @author mengyb
 * @since 2022-12-29
 */
public interface ISysScheduleTriggerService extends IService<SysScheduleTrigger> {
    //更新任务
    void refreshTrigger();

    /**
     * 新增定时任务
     * @param sysScheduleTrigger
     * @return
     */
    public ResultBean insertScheduleTrigger(SysScheduleTrigger sysScheduleTrigger);


    /**
     * 添加定时任务
     */
    public void schedulerAdd(SysScheduleTrigger sysScheduleTrigger);

    /**
     * 删除定时任务
     * @param sysScheduleTrigger
     */
    public void schedulerDelete(SysScheduleTrigger sysScheduleTrigger);

    /**
     * 删除定时任务
     * @param id
     * @return
     */
    ResultBean deleteScheduleTrigger(int id);

   

    ResultBean updateScheduleStatus(int id, int status);

    /**
     * 修改定时任务
     * @param sysScheduleTrigger
     * @return
     */
    ResultBean updateScheduleTrigger(SysScheduleTrigger sysScheduleTrigger);

}

2.3.4、ServiceImpl实现层代码
package com.mengyb.myb.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.mengyb.myb.handler.MyException;
import com.mengyb.myb.pojo.ResultBean;
import com.mengyb.myb.pojo.SysScheduleTrigger;
import com.mengyb.myb.mapper.SysScheduleTriggerMapper;
import com.mengyb.myb.quartz.QuartzJobExecution;
import com.mengyb.myb.quartz.ScheduleUtils;
import com.mengyb.myb.quartz.SendClass;
import com.mengyb.myb.service.ISysScheduleTriggerService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mengyb.myb.utils.SecurityUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.ModelMap;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 * 任务记录表 服务实现类
 * </p>
 *
 * @author mengyb
 * @since 2022-12-29
 */
@Service
public class SysScheduleTriggerServiceImpl extends ServiceImpl<SysScheduleTriggerMapper, SysScheduleTrigger> implements ISysScheduleTriggerService {

    private static final Logger logger = LoggerFactory.getLogger(SysScheduleTriggerServiceImpl.class);
    @Autowired
    private SysScheduleTriggerMapper sysScheduleTriggerMapper;

    @Autowired
    private Scheduler scheduler;

    /**
     * 更新任务
     */
    @Override
    public void refreshTrigger() {
        // 查询任务列表
        List<SysScheduleTrigger> sysScheduleTriggerList = sysScheduleTriggerMapper.selectList(null);

        if (sysScheduleTriggerList != null) {
            // 存在任务
            for (SysScheduleTrigger item: sysScheduleTriggerList) {
                //1表示正常启用
                if (item.getStatus() == 1) {
                    //拿出一个任务,启动一个任务
                    try {
                        //1.定义JobDetail,将HelloJob类添加到JobDetail对象中(添加到Job清单)
                        //com.example.job.YRJob
                        JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(item.getJobClass()))
                                //任务的名字和组的名字(坑)
                                //任务名字不能相同
                                .withIdentity(item.getJobName(), item.getJobGroup())
                                .build();

                        //2.定义Trigger触发器,使用简单触发器,设置name/group
                        org.quartz.Trigger trigger = TriggerBuilder.newTrigger()
                                .withIdentity(item.getTriggerName(), item.getTriggerGroup())
                                .startNow()
                                .withSchedule(CronScheduleBuilder.cronSchedule(item.getCron()))
                                .build();

                        //3.创建scheduler调度器
                        scheduler.scheduleJob(jobDetail, trigger);
                        //启动调度器
                        scheduler.start();

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 新增定时任务
     * @param sysScheduleTrigger
     * @return
     */
    @Transactional
    @Override
    public ResultBean insertScheduleTrigger(SysScheduleTrigger sysScheduleTrigger) {
        // 首先查看定时任务是否重复
        LambdaQueryWrapper<SysScheduleTrigger> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SysScheduleTrigger::getJobName, sysScheduleTrigger.getJobName());
        List<SysScheduleTrigger> sysScheduleTriggers = sysScheduleTriggerMapper.selectList(lambdaQueryWrapper);
        if (sysScheduleTriggers == null || sysScheduleTriggers.isEmpty()) {
            // 校验cron表单时  cron 表达式的验证使用正则太麻烦,可以使用 Quartz 自带验证方法
            CronExpression.isValidExpression(sysScheduleTrigger.getCron());
            // 证明不存在该任务
            sysScheduleTrigger.setAccount(SecurityUtils.getUserId());
            sysScheduleTrigger.setCreateTime(LocalDateTime.now());
            // 保存该任务
            int result = sysScheduleTriggerMapper.insert(sysScheduleTrigger);
            // 判断是否保存成功,切是否启动该任务
            if (result > 0 && sysScheduleTrigger.getStatus() == 1) {
                // 添加到定时任务  传递执行的任务类,参数,表达式
                schedulerAdd(sysScheduleTrigger);

                return ResultBean.success("定时任务新增成功");
            } else {
                throw new MyException(500, "定时任务新增失败", "");
            }
        }

        return ResultBean.error("定时任务新增失败");
    }

    /**
     * 添加新的任务
     */
    @Override
    public void schedulerAdd(SysScheduleTrigger sysScheduleTrigger) {

        try {
            // 启动调度器
            scheduler.start();
            // 构建job信息

            //任务实例(JobDetail)
            JobDetail jobDetail = JobBuilder.newJob(QuartzJobExecution.class) //加载任务类 完成与HelloJob的绑定 要求 HelloJob必须实现Job接口
                    .withIdentity(sysScheduleTrigger.getJobName(),sysScheduleTrigger.getJobGroup()) //参数1 任务的名称 参数二 任务组的名称
                    .usingJobData("messagel","打印日志jobDetail")
                    .build();

            // 放入参数,运行时的方法可以获取
            jobDetail.getJobDataMap().put("mengybQuratz", sysScheduleTrigger);

            // 任务组的名称
            //触发器(Triger)
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(sysScheduleTrigger.getTriggerName()) //参数1触发器的名称 参数2 触发器组的名称
                    .startNow()  //马上启动触发器
                    .withSchedule(CronScheduleBuilder.cronSchedule(sysScheduleTrigger.getCron()))
                    .usingJobData("message","打印日志Trigger")
                    .build();
            //让调度器关联任务和触发器,保证按照触发器的条件执行任务
            scheduler.scheduleJob(jobDetail, trigger);


        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    /**
     * 删除定时任务
     * @param sysScheduleTrigger
     */
    @Override
    public void schedulerDelete(SysScheduleTrigger sysScheduleTrigger) {
        JobKey jobKey = JobKey.jobKey(sysScheduleTrigger.getJobName(), sysScheduleTrigger.getJobGroup());
        try {
            boolean exists = scheduler.checkExists(jobKey);
            if(!exists){
                return;
            }
            scheduler.deleteJob(jobKey);
        }catch (Exception e){
            throw new RuntimeException("删除定时任务失败", e);
        }
    }

    @Transactional
    @Override
    public ResultBean deleteScheduleTrigger(int id) {
        // 先暂停任务  再删除
        SysScheduleTrigger scheduleTrigger = sysScheduleTriggerMapper.selectById(id);
        if (Objects.isNull(scheduleTrigger)) {
            // 该任务不存在
            throw new MyException(500, "该任务不存在", "");
        }
        int result = sysScheduleTriggerMapper.deleteById(id);
        // 删除任务
        schedulerDelete(scheduleTrigger);
        return result > 0 ? ResultBean.success("删除成功") : ResultBean.error("定时任务删除失败");
    }


   

    @Transactional
    @Override
    public ResultBean updateScheduleStatus(int id, int status) {
        SysScheduleTrigger sysScheduleTrigger = sysScheduleTriggerMapper.selectById(id);
        LambdaUpdateWrapper<SysScheduleTrigger> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(SysScheduleTrigger::getStatus, status);
        updateWrapper.eq(SysScheduleTrigger::getId, id);
        SysScheduleTrigger scheduleTrigger = new SysScheduleTrigger();
        // 先暂停任务  再删除

        JobKey jobKey = JobKey.jobKey(sysScheduleTrigger.getJobName(), sysScheduleTrigger.getJobGroup());

        // 判断任务是否存在
        boolean exists = false;
        try {
            exists = scheduler.checkExists(jobKey);
            if(!exists){
                throw new RuntimeException("该定时任务不存在");
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        if (status == 1) {
            // 恢复任务
            try {
                scheduler.resumeJob(jobKey);
            } catch (SchedulerException e) {
                throw new RuntimeException("恢复定时任务失败", e);
            }
            logger.info("开启任务:{}");
        } else {
            // 停止任务
            try {
                scheduler.pauseJob(jobKey);
            }catch (SchedulerException e){
                throw new RuntimeException("暂停定时任务失败", e);
            }
            logger.info("停止任务:{}");
        }
        int result = sysScheduleTriggerMapper.update(scheduleTrigger, updateWrapper);
        if (status == 1) {
            return result > 0 ? ResultBean.success("任务恢复成功") : ResultBean.error("任务恢复失败");
        } else {
            return result > 0 ? ResultBean.success("任务暂停成功") : ResultBean.error("任务暂停失败");
        }

    }

    /**
     * 修改定时任务
     * @param sysScheduleTrigger
     * @return
     */
    @Override
    public ResultBean updateScheduleTrigger(SysScheduleTrigger sysScheduleTrigger) {
        // 修改定时任务信息

        return null;
    }

    /**
     * 通过反射获取Bean
     * @param className
     * @return
     * @throws Exception
     */
    private static Job getClass(String className) throws Exception {
        Class<?> class1 = Class.forName(className);
        return (Job) class1.newInstance();
    }
}



2.3.5、Controller层代码
package com.mengyb.myb.controller;


import com.mengyb.myb.aop.OperatorLog;
import com.mengyb.myb.pojo.ResultBean;
import com.mengyb.myb.pojo.SysScheduleTrigger;
import com.mengyb.myb.service.ISysScheduleTriggerService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * <p>
 * 任务记录表 前端控制器
 * </p>
 *
 * @author mengyb
 * @since 2022-12-29
 */
@Api(value = "定时任务模块")
@RestController
@RequestMapping("/schedule")
public class SysScheduleTriggerController {

    @Autowired
    private ISysScheduleTriggerService sysScheduleTriggerService;

    

    /**
     * 新增定时任务  com.mengyb.myb.quartz.QuartzJob
     * @param sysScheduleTrigger
     * @return
     */
    @OperatorLog(operate="新增定时任务", module="定时任务模块")
    @ApiOperation(value = "新增定时任务")
    @PostMapping("/insertScheduleTrigger")
    public ResultBean insertScheduleTrigger(SysScheduleTrigger sysScheduleTrigger) {
        ResultBean resultBean = sysScheduleTriggerService.insertScheduleTrigger(sysScheduleTrigger);
        return resultBean;
    }


    @OperatorLog(operate="修改定时任务", module="定时任务模块")
    @ApiOperation(value = "修改定时任务")
    @PostMapping("/updateScheduleTrigger/{id}")
    public ResultBean updateScheduleTrigger(SysScheduleTrigger sysScheduleTrigger, @PathVariable("id") int id) {
        ResultBean resultBean = sysScheduleTriggerService.updateScheduleTrigger(sysScheduleTrigger);
        return resultBean;
    }


    @OperatorLog(operate="删除定时任务", module="定时任务模块")
    @ApiOperation(value = "删除定时任务")
    @GetMapping("/deleteScheduleTrigger/{id}")
    public ResultBean deleteScheduleTrigger(@PathVariable("id") int id) {
        ResultBean resultBean = sysScheduleTriggerService.deleteScheduleTrigger(id);
        return resultBean;
    }


}


【注】: 以上为基本的创建信息

2.4、开始创建Quartz相关的代码

2.4.1、创建实现Job接口的任务
package com.mengyb.myb.quartz;

import com.mengyb.myb.pojo.SysScheduleTrigger;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

public abstract class QuartzJob implements Job {
    private static final Logger logger = LoggerFactory.getLogger(QuartzJob.class);
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {

        //获取JobDetail的内容
        JobKey jobKey = context.getJobDetail().getKey();
        //获取Trigger内容
        TriggerKey triggerKey = context.getTrigger().getKey();
        //从Trigger对象中获取jobDataMap数据
        JobDataMap triggerDataMap = context.getTrigger().getJobDataMap();
        // 根据反射去执行对应的任务
        SysScheduleTrigger sysJob = new SysScheduleTrigger();
        // 注意该处是之前将我们自己定义的任务信息表数据 追加到了jobDetail中  在创建JobDetail后追加 jobDetail.getJobDataMap().put("mengybQuratz", sysScheduleTrigger);
        Object mengybQuratz = context.getMergedJobDataMap().get("mengybQuratz");
        BeanUtils.copyProperties(mengybQuratz, sysJob);
        try
        {
            if (sysJob != null)
            {
            	// 执行自己定义的类
                doExecute(context, sysJob);
            }
        }
        catch (Exception e)
        {
            logger.error("任务执行异常  - :", e);
        }
    }



    /**
     * 执行方法,由子类重载
     *
     * @param context 工作执行上下文对象
     * @param sysJob 系统计划任务
     * @throws Exception 执行过程中的异常
     */
    protected abstract void doExecute(JobExecutionContext context, SysScheduleTrigger sysJob) throws Exception;
}

重载execute(JobExecutionContext context, SysScheduleTrigger sysJob)方法

package com.mengyb.myb.quartz;

import com.mengyb.myb.pojo.SysScheduleTrigger;
import org.quartz.JobExecutionContext;

/**
 * 定时任务处理(允许并发执行)
 * 
 * @author mengyb
 * @since 2022-12-29
 */
public class QuartzJobExecution extends QuartzJob
{
    @Override
    protected void doExecute(JobExecutionContext context, SysScheduleTrigger sysJob) throws Exception {
        JobInvokeUtil.invokeMethod(sysJob);
    }
}

2.4.2、工具类,为反射操作做准备
package com.mengyb.myb.quartz;

import com.mengyb.myb.pojo.SysScheduleTrigger;
import com.mengyb.myb.utils.StringUtils;
import com.mengyb.myb.utils.spring.SpringUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

/**
 * 任务执行工具
 *
 * @author mengyb
 * @since 2022-12-29
 */
public class JobInvokeUtil
{
    /**
     * 执行方法
     *
     * @param sysJob 系统任务
     */
    public static void invokeMethod(SysScheduleTrigger sysJob) throws Exception
    {
        // 类路径
        String invokeTarget = sysJob.getJobClass();
        // 获取Bean名称
        String beanName = sysJob.getJobClass();
        // 方法名称
        String methodName = sysJob.getJobMethod();
        // 获取参数列表及参数类型
        List<Object[]> methodParams = getMethodParams(sysJob.getJobParams());

        if (!isValidClassName(beanName))
        {
            Object bean = SpringUtils.getBean(beanName);
            invokeMethod(bean, methodName, methodParams);
        }
        else
        {
            Object bean = Class.forName(beanName).newInstance();
            invokeMethod(bean, methodName, methodParams);
        }
    }

    /**
     * 调用任务方法
     *
     * @param bean 目标对象
     * @param methodName 方法名称
     * @param methodParams 方法参数
     */
    private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
            throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
            InvocationTargetException
    {
        if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0)
        {
            Method method = bean.getClass().getMethod(methodName, getMethodParamsType(methodParams));
            method.invoke(bean, getMethodParamsValue(methodParams));
        }
        else
        {
            Method method = bean.getClass().getMethod(methodName);
            method.invoke(bean);
        }
    }

    /**
     * 校验是否为为class包名
     * 
     * @param invokeTarget 名称
     * @return true是 false否
     */
    public static boolean isValidClassName(String invokeTarget)
    {
        return StringUtils.countMatches(invokeTarget, ".") > 1;
    }

    /**
     * 获取bean名称
     * 
     * @param invokeTarget 目标字符串
     * @return bean名称
     */
    public static String getBeanName(String invokeTarget)
    {
        String beanName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringBeforeLast(beanName, ".");
    }

    /**
     * 获取bean方法
     * 
     * @param invokeTarget 目标字符串
     * @return method方法
     */
    public static String getMethodName(String invokeTarget)
    {
        String methodName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringAfterLast(methodName, ".");
    }

    /**
     * 获取method方法参数相关列表
     * 
     * @param params 目标字符串
     * @return method方法相关参数列表
     */
    public static List<Object[]> getMethodParams(String params)
    {
        String methodStr = params;
        if (StringUtils.isEmpty(methodStr))
        {
            return null;
        }
        String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)");
        List<Object[]> classs = new LinkedList<>();
        for (int i = 0; i < methodParams.length; i++)
        {
            String str = StringUtils.trimToEmpty(methodParams[i]);
            // String字符串类型,以'或"开头
            if (StringUtils.startsWithAny(str, "'", "\""))
            {
                classs.add(new Object[] { StringUtils.substring(str, 1, str.length() - 1), String.class });
            }
            // boolean布尔类型,等于true或者false
            else if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str))
            {
                classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });
            }
            // long长整形,以L结尾
            else if (StringUtils.endsWith(str, "L"))
            {
                classs.add(new Object[] { Long.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Long.class });
            }
            // double浮点类型,以D结尾
            else if (StringUtils.endsWith(str, "D"))
            {
                classs.add(new Object[] { Double.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Double.class });
            }
            // 其他类型归类为整形
            else
            {
                classs.add(new Object[] { Integer.valueOf(str), Integer.class });
            }
        }
        return classs;
    }

    /**
     * 获取参数类型
     * 
     * @param methodParams 参数相关列表
     * @return 参数类型列表
     */
    public static Class<?>[] getMethodParamsType(List<Object[]> methodParams)
    {
        Class<?>[] classs = new Class<?>[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams)
        {
            classs[index] = (Class<?>) os[1];
            index++;
        }
        return classs;
    }

    /**
     * 获取参数值
     * 
     * @param methodParams 参数相关列表
     * @return 参数值列表
     */
    public static Object[] getMethodParamsValue(List<Object[]> methodParams)
    {
        Object[] classs = new Object[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams)
        {
            classs[index] = (Object) os[0];
            index++;
        }
        return classs;
    }
}

spring工具类

package com.mengyb.myb.utils.spring;

import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * spring工具类 方便在非spring管理环境中获取bean
 * 
 * @author mengyb
 * @since 2022-12-29
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
{
    /** Spring应用上下文环境 */
    private static ConfigurableListableBeanFactory beanFactory;

    private static ApplicationContext applicationContext;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
    {
        SpringUtils.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
    {
        SpringUtils.applicationContext = applicationContext;
    }

    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException
    {
        return (T) beanFactory.getBean(name);
    }

    /**
     * 获取类型为requiredType的对象
     *
     * @param clz
     * @return
     * @throws BeansException
     *
     */
    public static <T> T getBean(Class<T> clz) throws BeansException
    {
        T result = (T) beanFactory.getBean(clz);
        return result;
    }

    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name)
    {
        return beanFactory.containsBean(name);
    }

    /**
     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.isSingleton(name);
    }

    /**
     * @param name
     * @return Class 注册对象的类型
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getType(name);
    }

    /**
     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     * @return
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getAliases(name);
    }

    /**
     * 获取aop代理对象
     * 
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker)
    {
        return (T) AopContext.currentProxy();
    }

    /**
     * 获取当前的环境配置,无配置返回null
     *
     * @return 当前的环境配置
     */
    public static String[] getActiveProfiles()
    {
        return applicationContext.getEnvironment().getActiveProfiles();
    }



}

StringUtils工具类

package com.mengyb.myb.utils;

import org.springframework.util.AntPathMatcher;

import java.util.*;

/**
 * 字符串工具类
 * 
 * @author mengyb
 * @since 2022-12-29
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils
{
    /** 空字符串 */
    private static final String NULLSTR = "";

    /** 下划线 */
    private static final char SEPARATOR = '_';

    /**
     * 获取参数不为空值
     * 
     * @param value defaultValue 要判断的value
     * @return value 返回值
     */
    public static <T> T nvl(T value, T defaultValue)
    {
        return value != null ? value : defaultValue;
    }

    /**
     * * 判断一个Collection是否为空, 包含List,Set,Queue
     * 
     * @param coll 要判断的Collection
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(Collection<?> coll)
    {
        return isNull(coll) || coll.isEmpty();
    }

    /**
     * * 判断一个Collection是否非空,包含List,Set,Queue
     * 
     * @param coll 要判断的Collection
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Collection<?> coll)
    {
        return !isEmpty(coll);
    }

    /**
     * * 判断一个对象数组是否为空
     * 
     * @param objects 要判断的对象数组
     ** @return true:为空 false:非空
     */
    public static boolean isEmpty(Object[] objects)
    {
        return isNull(objects) || (objects.length == 0);
    }

    /**
     * * 判断一个对象数组是否非空
     * 
     * @param objects 要判断的对象数组
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Object[] objects)
    {
        return !isEmpty(objects);
    }

    /**
     * * 判断一个Map是否为空
     * 
     * @param map 要判断的Map
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(Map<?, ?> map)
    {
        return isNull(map) || map.isEmpty();
    }

    /**
     * * 判断一个Map是否为空
     * 
     * @param map 要判断的Map
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Map<?, ?> map)
    {
        return !isEmpty(map);
    }

    /**
     * * 判断一个字符串是否为空串
     * 
     * @param str String
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(String str)
    {
        return isNull(str) || NULLSTR.equals(str.trim());
    }

    /**
     * * 判断一个字符串是否为非空串
     * 
     * @param str String
     * @return true:非空串 false:空串
     */
    public static boolean isNotEmpty(String str)
    {
        return !isEmpty(str);
    }

    /**
     * * 判断一个对象是否为空
     * 
     * @param object Object
     * @return true:为空 false:非空
     */
    public static boolean isNull(Object object)
    {
        return object == null;
    }

    /**
     * * 判断一个对象是否非空
     * 
     * @param object Object
     * @return true:非空 false:空
     */
    public static boolean isNotNull(Object object)
    {
        return !isNull(object);
    }

    /**
     * * 判断一个对象是否是数组类型(Java基本型别的数组)
     * 
     * @param object 对象
     * @return true:是数组 false:不是数组
     */
    public static boolean isArray(Object object)
    {
        return isNotNull(object) && object.getClass().isArray();
    }

    /**
     * 去空格
     */
    public static String trim(String str)
    {
        return (str == null ? "" : str.trim());
    }

    /**
     * 截取字符串
     * 
     * @param str 字符串
     * @param start 开始
     * @return 结果
     */
    public static String substring(final String str, int start)
    {
        if (str == null)
        {
            return NULLSTR;
        }

        if (start < 0)
        {
            start = str.length() + start;
        }

        if (start < 0)
        {
            start = 0;
        }
        if (start > str.length())
        {
            return NULLSTR;
        }

        return str.substring(start);
    }

    /**
     * 截取字符串
     * 
     * @param str 字符串
     * @param start 开始
     * @param end 结束
     * @return 结果
     */
    public static String substring(final String str, int start, int end)
    {
        if (str == null)
        {
            return NULLSTR;
        }

        if (end < 0)
        {
            end = str.length() + end;
        }
        if (start < 0)
        {
            start = str.length() + start;
        }

        if (end > str.length())
        {
            end = str.length();
        }

        if (start > end)
        {
            return NULLSTR;
        }

        if (start < 0)
        {
            start = 0;
        }
        if (end < 0)
        {
            end = 0;
        }

        return str.substring(start, end);
    }





    /**
     * 判断给定的set列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
     *
     * @param
     * @param array 给定的数组
     * @return boolean 结果
     */
    public static boolean containsAny(Collection<String> collection, String... array)
    {
        if (isEmpty(collection) || isEmpty(array))
        {
            return false;
        }
        else
        {
            for (String str : array)
            {
                if (collection.contains(str))
                {
                    return true;
                }
            }
            return false;
        }
    }

    /**
     * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
     *
     * @param cs 指定字符串
     * @param searchCharSequences 需要检查的字符串数组
     * @return 是否包含任意一个字符串
     */
    public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
    {
        if (isEmpty(cs) || isEmpty(searchCharSequences))
        {
            return false;
        }
        for (CharSequence testStr : searchCharSequences)
        {
            if (containsIgnoreCase(cs, testStr))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 驼峰转下划线命名
     */
    public static String toUnderScoreCase(String str)
    {
        if (str == null)
        {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        // 前置字符是否大写
        boolean preCharIsUpperCase = true;
        // 当前字符是否大写
        boolean curreCharIsUpperCase = true;
        // 下一字符是否大写
        boolean nexteCharIsUpperCase = true;
        for (int i = 0; i < str.length(); i++)
        {
            char c = str.charAt(i);
            if (i > 0)
            {
                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
            }
            else
            {
                preCharIsUpperCase = false;
            }

            curreCharIsUpperCase = Character.isUpperCase(c);

            if (i < (str.length() - 1))
            {
                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
            }

            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
            {
                sb.append(SEPARATOR);
            }
            else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
            {
                sb.append(SEPARATOR);
            }
            sb.append(Character.toLowerCase(c));
        }

        return sb.toString();
    }

    /**
     * 是否包含字符串
     * 
     * @param str 验证字符串
     * @param strs 字符串组
     * @return 包含返回true
     */
    public static boolean inStringIgnoreCase(String str, String... strs)
    {
        if (str != null && strs != null)
        {
            for (String s : strs)
            {
                if (str.equalsIgnoreCase(trim(s)))
                {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
     * 
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String convertToCamelCase(String name)
    {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty())
        {
            // 没必要转换
            return "";
        }
        else if (!name.contains("_"))
        {
            // 不含下划线,仅将首字母大写
            return name.substring(0, 1).toUpperCase() + name.substring(1);
        }
        // 用下划线将原始字符串分割
        String[] camels = name.split("_");
        for (String camel : camels)
        {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty())
            {
                continue;
            }
            // 首字母大写
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        return result.toString();
    }

    /**
     * 驼峰式命名法 例如:user_name->userName
     */
    public static String toCamelCase(String s)
    {
        if (s == null)
        {
            return null;
        }
        s = s.toLowerCase();
        StringBuilder sb = new StringBuilder(s.length());
        boolean upperCase = false;
        for (int i = 0; i < s.length(); i++)
        {
            char c = s.charAt(i);

            if (c == SEPARATOR)
            {
                upperCase = true;
            }
            else if (upperCase)
            {
                sb.append(Character.toUpperCase(c));
                upperCase = false;
            }
            else
            {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
     * 
     * @param str 指定字符串
     * @param strs 需要检查的字符串数组
     * @return 是否匹配
     */
    public static boolean matches(String str, List<String> strs)
    {
        if (isEmpty(str) || isEmpty(strs))
        {
            return false;
        }
        for (String pattern : strs)
        {
            if (isMatch(pattern, str))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断url是否与规则配置: 
     * ? 表示单个字符; 
     * * 表示一层路径内的任意字符串,不可跨层级; 
     * ** 表示任意层路径;
     * 
     * @param pattern 匹配规则
     * @param url 需要匹配的url
     * @return
     */
    public static boolean isMatch(String pattern, String url)
    {
        AntPathMatcher matcher = new AntPathMatcher();
        return matcher.match(pattern, url);
    }

    @SuppressWarnings("unchecked")
    public static <T> T cast(Object obj)
    {
        return (T) obj;
    }

    /**
     * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
     * 
     * @param num 数字对象
     * @param size 字符串指定长度
     * @return 返回数字的字符串格式,该字符串为指定长度。
     */
    public static final String padl(final Number num, final int size)
    {
        return padl(num.toString(), size, '0');
    }

    /**
     * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
     * 
     * @param s 原始字符串
     * @param size 字符串指定长度
     * @param c 用于补齐的字符
     * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
     */
    public static final String padl(final String s, final int size, final char c)
    {
        final StringBuilder sb = new StringBuilder(size);
        if (s != null)
        {
            final int len = s.length();
            if (s.length() <= size)
            {
                for (int i = size - len; i > 0; i--)
                {
                    sb.append(c);
                }
                sb.append(s);
            }
            else
            {
                return s.substring(len - size, len);
            }
        }
        else
        {
            for (int i = size; i > 0; i--)
            {
                sb.append(c);
            }
        }
        return sb.toString();
    }
}
2.4.3、通过凡是调用的方法
package com.mengyb.myb.quartz;

/**
 * @author mengyb
 * @since 2022-12-29
 */
public class SendClass {
    public void send1() {
        System.out.println("定时任务调用的方法1");
    }
    public void send2(String name) {
        System.out.println("定时任务调用的方法2---" + name);
    }
}

2.5、系统启动任务

package com.mengyb.myb.quartz;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mengyb.myb.pojo.SysScheduleTrigger;
import com.mengyb.myb.service.ISysScheduleTriggerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 当我们添加了定时任务并启动后,重新启动项目的时候,定时任务却不会自动启动,这时候,我们就需要在项目启动的时候做一些事情了,也就是系统启动任务
 * SpringBoot 中提供了两种项目启动方案,CommandLineRunner 和 ApplicationRunner
 * @author mengyb
 * @since 2022-12-29
 */
@Component // @Component 将该类注册成为 Spring 容器中的一个 Bean
@Order(100) // @Order(100) 标明该启动任务的优先级,值越大,表示优先级越小
public class QuartzTask implements CommandLineRunner {

    @Autowired
    private ISysScheduleTriggerService sysScheduleTriggerService;

    /**
     * 从数据库查询出启动的定时任务,并将他们添加到定时任务启动中,这样项目一启动时,就会自动启动我们定义的定时任务了
     * @param args
     * @throws Exception
     */
    @Override
    public void run(String... args) throws Exception {
        LambdaQueryWrapper<SysScheduleTrigger> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SysScheduleTrigger::getStatus, 1);
        List<SysScheduleTrigger> scheduleTriggerList = sysScheduleTriggerService.list(lambdaQueryWrapper);
        if (null != scheduleTriggerList && !scheduleTriggerList.isEmpty()) {
            for (SysScheduleTrigger item: scheduleTriggerList) {
                // 首先删除定时任务
                sysScheduleTriggerService.schedulerDelete(item);
                // 再添加定时任务
                sysScheduleTriggerService.schedulerAdd(item);
            }
        }

    }
}

三、启动项目

结果展示
Quartz持久化结果展示

【注】:使用持久化调用自己规定的方法及逻辑,需要使用到反射技术。
反射技术的掌握对于学习源码是有很大的帮助的,同样反射在日常开发中也可以进行使用,反射可以获取到对应的类中的所有信息。

可能整理的过程不是那么的简介,后期会再次进行优化。希望大家多多指导

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值