Quartz定时任务管理

Quartz定时任务管理(启动、停止、恢复、删除定时任务)


Quartz定时任务主要由Scheduler、JobDetail、CronTrigger、Cron组成,实现动态管理定时任务,主要就是通过管理上述对象来实现的。

一、引入jar

<!-- 定时任务 -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.mchange</groupId>
                    <artifactId>c3p0</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

二、Table

CREATE TABLE `sys_job` (
  `job_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务ID',
  `job_name` varchar(64) DEFAULT '' COMMENT '任务名称',
  `job_group` varchar(64) DEFAULT 'DEFAULT' COMMENT '任务组名',
  `invoke_target` varchar(500) DEFAULT NULL COMMENT '调用目标 列:类名.方法名',
  `cron_expression` varchar(255) DEFAULT '' COMMENT 'cron执行表达式',
  `status` char(1) DEFAULT '0' COMMENT '状态(0正常 1暂停)',
  `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `remark` varchar(500) DEFAULT '' COMMENT '备注信息',
  `isdel` int(2) DEFAULT '0' COMMENT '是否删除 0未删除,1已删除',
  PRIMARY KEY (`job_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='定时任务调度表';

在这里插入图片描述

三、创建配置文件文件

QuartzConfig:
在Configuration中配置Scheduler实例,并启动。

/**
 * 定时任务配置(单机部署建议删除此类和qrtz数据库表,默认走内存会最高效)
 *
 * @author huang
 */
@Configuration
public class QuartzConfig {

    @Bean
    public Scheduler scheduler(){
        Scheduler scheduler = null;
        SchedulerFactory factory = new StdSchedulerFactory();
        try {
            scheduler = factory.getScheduler();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        if(scheduler != null){
            try {
                //启动定时任务
                scheduler.start();
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
        }
        return scheduler;
    }
}

JobInvokeUtil:

/**
 * 任务执行工具
 * @author huangjj
 */
public class JobInvokeUtil
{

    public static String TASK_PROPERTIES = "TASK_PROPERTIES";
    /**
     * 执行方法
     *
//     * @param sysJob 系统任务
     */
    public static Object invokeMethod(SysJob sysJob) throws Exception
    {
        String invokeTarget = sysJob.getInvokeTarget();
        String beanName = getBeanName(invokeTarget);
        String methodName = getMethodName(invokeTarget);
        List<Object[]> methodParams = getMethodParams(invokeTarget);
        Object bean = null;
        if (!isValidClassName(beanName))
        {
            bean = SpringUtils.getBean(beanName);
            invokeMethod(bean, methodName, methodParams);
        }
        else
        {
            bean = Class.forName(beanName).newInstance();
            invokeMethod(bean, methodName, methodParams);
        }
        return bean;
    }

    /**
     * 调用任务方法
     *
     * @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().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
            method.invoke(bean, getMethodParamsValue(methodParams));
        }
        else
        {
            Method method = bean.getClass().getDeclaredMethod(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 invokeTarget 目标字符串
     * @return method方法相关参数列表
     */
    public static List<Object[]> getMethodParams(String invokeTarget)
    {
        String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
        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;
    }
}

QuartzJob:
任务类JOB就是定时任务具体要处理的系统业务逻辑,需要实现Job接口。在任务启动时,通过jobClass传入JobDetail。

/**
 * 抽象quartz调用
 *
 * @author huang
 */
public abstract class QuartzJob implements Job {

    private static final Logger log = LoggerFactory.getLogger(QuartzJob.class);

    @Override
    public void execute(JobExecutionContext context) {
        try{
            SysJob sysJob = new SysJob();
            BeanUtils.copyProperties(context.getMergedJobDataMap().get(JobInvokeUtil.TASK_PROPERTIES),sysJob);
            doExecute(context,sysJob);
            System.out.println("执行定时任务:"+new Date());
        }catch (Exception e){
            log.error("任务执行异常  - :", e);
            e.printStackTrace();
        }
    }

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

QuartzJobExecution:

/**
 * 定时任务处理(允许并发执行)
 * 
 * @author ruoyi
 *
 */
public class QuartzJobExecution extends QuartzJob
{

    @Override
    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
    {
        JobInvokeUtil.invokeMethod(sysJob);
    }
}

四、业务实现

QuartzService :

/**
 * 定时任务管理服务
 */
@Service
public class QuartzService {

    @Autowired
    private Scheduler scheduler;

    /**
     * 启动任务
     * @param jobId 任务ID
     * @param jobGroup 任务名称
     * @param cron 执行正则
     * @throws Exception
     */
    public void startJob(String jobId, String cron, String jobGroup) throws Exception{
        Class<? extends Job> jobClass = QuartzJobExecution.class;//获取任务执行类
        //创建job,指定job名称和分组
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobId, jobGroup).build();
        //创建表达式工作计划
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
        //创建触发器
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobId, jobGroup)
                .withSchedule(cronScheduleBuilder).build();
      // TODO jobId:sys_job的ID,sysJobMapper数据层查询任务数据
       SysJob job = sysJobMapper.selectById(jobId);
       // 放入参数,运行时的方法可以获取
       jobDetail.getJobDataMap().put(JobInvokeUtil.TASK_PROPERTIES, job);
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }

    /**
     * 修改定时任务执行时间
     * @param jobId 任务ID
     * @param jobGroup 任务名称
     * @param cron 执行正则
     * @throws Exception
     */
    public void modifyJob(String jobId, String jobGroup, String cron) throws Exception{
        TriggerKey triggerKey = new TriggerKey(jobId, jobGroup);
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        String oldCron = trigger.getCronExpression();
        if(!oldCron.equals(cron)){
            CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobId, jobGroup)
                    .withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
            Date date = scheduler.rescheduleJob(triggerKey, cronTrigger);
            if(date == null){
                throw new Exception("修改定时任务执行时间报错");
            }
        }
    }

    /**
     * 暂停某个定时任务(任务恢复后,暂停时间段内未执行的任务会继续执行,如暂停时间段内有2次,则会执行2次)
     * @param jobId 任务ID
     * @param jobGroup 任务名称
     * @throws Exception
     */
    public void pauseJob(String jobId, String jobGroup) throws Exception{
        JobKey jobKey = new JobKey(jobId, jobGroup);
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复某个定时任务
     * @param jobId 任务ID
     * @param jobGroup 任务名称
     * @throws Exception
     */
    public void resumeJob(String jobId, String jobGroup) throws Exception{
        JobKey jobKey = new JobKey(jobId, jobGroup);
        scheduler.resumeJob(jobKey);
    }

    /**
     * 删除某个定时任务
     * @param jobId 任务ID
     * @param jobGroup 任务名称
     * @throws Exception
     */
    public void deleteJob(String jobId, String jobGroup) throws Exception{
        JobKey jobKey = new JobKey(jobId, jobGroup);
        scheduler.deleteJob(jobKey);
    }
}

TestTask:

/**
 * 定时任务调度测试
 * 
 * @author huang
 */
@Component("task")
public class Task
{
    public void multipleParams(String s, Boolean b, Long l, Double d, Integer i)
    {
        System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
    }

    public void params(String params)
    {
        System.out.println("执行有参方法:" + params);
    }

    public void noParams()
    {
        System.out.println("执行无参方法noParams"+new Date());
    }
    public void noParamsOne()
    {
        System.out.println("执行无参方法noParamsOne"+new Date());
    }
}

五、编写API接口

通过Controller提供API接口,这里我的TaskService调用了QartzService的对应接口,并做了一个写数据库读写操作,主要记录定时任务状态、执行记录信息的等。

@RestController
@RequestMapping("/quartz")
public class QuartzController {

    @Autowired
    private QuartzService service;

    @RequestMapping("/start")
    public Object start(String id,String cron,String jobGroup){
        try {
            service.startJob(id,cron,jobGroup);
            return "ok";
        } catch (Exception e) {
            return "error";
        }
    }

    @RequestMapping("/pause")
    public Object pause(String id, String jobGroup){
        try {
            service.pauseJob(id,jobGroup);
            return "ok";
        } catch (Exception e) {
            return "error";
        }
    }

    @RequestMapping("/resume")
    public Object resume(String id, String jobGroup){
        try {
            service.resumeJob(id,jobGroup);
            return "ok";
        } catch (Exception e) {
            return "error";
        }
    }

    @RequestMapping("/remove")
    public Object remove(String id, String jobGroup){
        try {
            service.deleteJob(id,jobGroup);
            return "ok";
        } catch (Exception e) {
            return "error";
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值