Spring / SpringBoot集成Quartz实现动态管理定时任务

8 篇文章 0 订阅

前言:
最近在做Java开发对接微信公众平台之类的工作,在开发过程中遇到一个需求是定时任务这块的,但是普通的定时任务还远远不能满足;最后要实现的效果是每个任务都是不同的定时时间来执行而且是在前台页面上来控制这些任务的创建,取消,开启,暂停;就好比是向数据库添加一条记录(任务)那么在添加成功后这个任务就是进入定时执行状态了,每次执行的时间是由添加任务时前台页面选择的时间;

要实现的效果如下:

在这里插入图片描述
在这里插入图片描述
该项目的整体框架和环境选用的时SpringBoot,所以当时在网上看了大部分的boot整合Quartz的文章发现都存在bug和漏洞并且很麻烦和繁琐;通过在网上参考相关的文章加上之前积攒的知识,整合了一套简单并且实用的以上需求相关代码,可以参考。

pom:

<dependency>
   	<groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.1</version>
</dependency>

在项目的基础上需要添加上quartz的依赖包;(其他boot之类的依赖包就不一一说明了)

需要在启动类上开启定时任务
在这里插入图片描述
定时任务管理类:


import com.redleaves.wechat.api.core.Result;
import com.redleaves.wechat.api.core.ResultGenerator;
import com.redleaves.wechat.api.model.TTemplateMessage;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author Mr.Gu
 * @date 2020/8/3 16:17
 * @function :  定时任务的管理
 **/
@Slf4j
@Service
public class SchedulerBuilder {

    @Autowired
    private Scheduler scheduler;

    /**
     * 新建job
     * @param job
     * @param clazz
     * @return
     */
    public Result createJob(TTemplateMessage job, Class clazz) {

        try {
            if (!((clazz.newInstance()) instanceof Job)){
                return ResultGenerator.genFailResult("必须实现job接口");
            }

            TriggerKey triggerKey = TriggerKey.triggerKey(job.getId().toString());

            //获取trigger
            CronTrigger trigger = null;

            trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            //不存在,创建一个
            if (null == trigger) {
                JobDetail jobDetail = JobBuilder.newJob(clazz)
                        .withIdentity(job.getId().toString()).build();
                jobDetail.getJobDataMap().put("scheduleJob", job);

                //表达式调度构建器
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCorn());

                //按新的cronExpression表达式构建一个新的trigger
                trigger = TriggerBuilder.newTrigger().withIdentity(job.getId().toString()).withSchedule(scheduleBuilder).build();

                scheduler.scheduleJob(jobDetail, trigger);

                log.info("id为:{},推送名称为:{}的任务创建成功",job.getId(),job.getName());

                return ResultGenerator.genSuccessResult();
            }else{
                return ResultGenerator.genFailResult("job已经存在,请检查名称是否重复");
            }

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

    /**
     * 立即执行job  是对已经存在的定时任务执行
     * @param job
     * @return
     */
    public Result executeJob(TTemplateMessage job) {

        JobKey jobKey = JobKey.jobKey(job.getId().toString());
        try {
            scheduler.triggerJob(jobKey);
            return ResultGenerator.genSuccessResult();
        } catch (SchedulerException e) {
            e.printStackTrace();
            return ResultGenerator.genFailResult("操作失败");
        }
    }

    /**
     * 更新执行时间
     * @param job
     * @return
     */
    public Result updateJobCron(TTemplateMessage job) {

        TriggerKey triggerKey = TriggerKey.triggerKey(job.getId().toString());

        //获取trigger,即在spring配置文件中定义的 bean id="myTrigger"
        CronTrigger trigger = null;
        try {
            trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }

        //表达式调度构建器
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCorn());

        //按新的cronExpression表达式重新构建trigger
        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

        //按新的trigger重新设置job执行
        try {
            scheduler.rescheduleJob(triggerKey, trigger);
            return ResultGenerator.genSuccessResult();
        } catch (SchedulerException e) {
            e.printStackTrace();
            return ResultGenerator.genFailResult("操作失败");
        }
    }

    /**
     * 暂停job运行
     * @param job
     * @return*/

    public Result pauseJob(TTemplateMessage job) {

        JobKey jobKey = JobKey.jobKey(job.getId().toString());
        try {
            scheduler.pauseJob(jobKey);
            return ResultGenerator.genSuccessResult();
        } catch (SchedulerException e) {
            e.printStackTrace();
            return ResultGenerator.genFailResult("操作失败");
        }
    }

    /**
     * 恢复job运行
     * @param job
     * @return*/

    public Result resumeJob(TTemplateMessage job) {

        JobKey jobKey = JobKey.jobKey(job.getId().toString());
        try {
            scheduler.resumeJob(jobKey);
            return ResultGenerator.genSuccessResult();
        } catch (SchedulerException e) {
            e.printStackTrace();
            return ResultGenerator.genFailResult("操作失败");
        }
    }

    /* *
     * 删除job信息
     * @param job
     * @return*/

    public Result delJob(TTemplateMessage job) {

        JobKey jobKey = JobKey.jobKey(job.getId().toString());
        TriggerKey triggerKey = TriggerKey.triggerKey(job.getId().toString());
        try {
            scheduler.pauseTrigger(triggerKey);// 停止触发器
            scheduler.unscheduleJob(triggerKey);// 移除触发器
            scheduler.deleteJob(jobKey);//删除job
            return ResultGenerator.genSuccessResult();
        } catch (SchedulerException e) {
            e.printStackTrace();
            return ResultGenerator.genFailResult("操作失败");
        }
    }
}

任务触发后具体的执行逻辑

import com.redleaves.wechat.api.dao.TTemplateMessageMapper;
import com.redleaves.wechat.api.model.TTemplateMessage;
import com.redleaves.wechat.api.service.SendTemplateService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import javax.annotation.Resource;

/**
 * @author Mr.Gu
 * @date 2020/8/3 13:36
 * @function :  定时任务触发器
 **/
@Slf4j
public class MyJob implements Job {

    private JobDataMap jobmap = null;

    /**
     * 定时任务触发后任务的具体执行逻辑
     * @param paramJobExecutionContext
     * @throws JobExecutionException
     */
    @Override
    public void execute(JobExecutionContext paramJobExecutionContext) throws JobExecutionException {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);

        TTemplateMessage tTemplateMessage = new TTemplateMessage();

        this.jobmap = paramJobExecutionContext.getJobDetail().getJobDataMap();
        tTemplateMessage = (TTemplateMessage) jobmap.get("scheduleJob");
        log.info("开始执行任务id:{};推送名称为:{}",tTemplateMessage.getId(),tTemplateMessage.getName());

        ----------------------任务的具体执行逻辑-----------------------------------
    }
}

在控制层中接收到请求后创建任务

@RestController
@RequestMapping("/template/message")
public class TTemplateMessageController {
    @Resource
    private TTemplateMessageService tTemplateMessageService;
    @Autowired
    private SendTemplateService sendTemplateService;
    @Resource
    private SchedulerBuilder schedulerBuilder;

    /**
     * 新建模板消息推送
     * @param templateMessageRequest
     * @return
     */
    @PostMapping("/sendTemplateMessage")
    public Result sendTemplateMessage(@RequestBody TTemplateMessage templateMessageRequest) {
        try {
        	schedulerBuilder.createJob(templateMessageRequest,MyJob.class);
            return ResultGenerator.genSuccessResult();
        } catch (Exception e) {
            e.printStackTrace();
            return ResultGenerator.genFailResult("操作失败");
        }
    }
  
}

以上就可以实现前言中所说的动态管理;就分享了可以实现定时任务部分,我是先将任务保存到数据库中,之后任务在execute()触发后进行具体执行相关逻辑处理的时候再从数据库中查询到;以及其他的相关处理逻辑可以根据自己的业务来添加;

如果是spring项目的话,使用方式和上面一样。不用编写quartz的配置文件但需要注意的是要在spring的配置文件中加上:

<bean id="scheduler" 
class="org.springframework.scheduling.quartz.SchedulerFactoryBean" />

要不然在SchedulerBuilder 无法注入Scheduler。

有一个需要注意的地方,就是服务重启后已经添加到触发器中的任务不会再执行,需要将任务重新添加到触发器中才能继续定时执行。
我的解决方案是:每次项目启动后都去数据库中查询出未执行的项目然后将这些未执行的任务重新添加到触发器中(相当于重新创建了任务)。

例:自定义监听器实现spring的ApplicationListener事件

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;

/**
 *  当spring容器初始化完成后就会执行该方法。
 *  项目启动后将任务表中未执行的任务重新注册到触发器中
 */
@Service
public class JobListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if(event.getApplicationContext().getParent() == null){

            //要执行的逻辑代码
        }
    }
}


以上分享希望可以帮助到正在做定时任务的你,如果其他更好的意见和改善望指点;

如果这篇文章帮助到了你 分享出去让他帮助更多正在挠头扣手的童鞋。

更多技术分享可以关注我的私人公众号,探索出Java更多的未知;
在这里插入图片描述

喜欢点赞、分享、加关注哦!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值