quartz-动态任务开发

quartz-动态任务开发

介绍:根据任务数据不同,执行不同的内容

案例主要技术栈:MybatisPlus + MySQL

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
    <version>2.1.3.RELEASE</version>
</dependency>

解决job实现类无法注入spring管理的bean

开发CustomJobFactory,继承AdaptableJobFactory

/**
 * @author riKylinz
 * @description 自定义job工厂,用于解决job实现类无法注入spring管理的bean  与QuartzConfig类连用
 * 并把SchedulerFactoryBean的job工厂设置成  自定义job工厂(CustomJobFactory)
 * @date 2022/9/13 11:51
 */
@Component
public class CustomJobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory autowireCapableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //调用父类方法  拿到属于Quartz的job实现类的实例
        Object jobInstance = super.createJobInstance(bundle);
        //把实例注入spring容器中
        autowireCapableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

开发quartz配置类,把自定义工厂类添加到SchedulerFactoryBean

/**
 * @author riKylinz
 * @description Quartz配置类
 * @date 2022/9/13 11:50
 */
@Configuration
public class QuartzConfig {

    @Autowired
    private CustomJobFactory customJobFactory;

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(){
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(customJobFactory);
        return schedulerFactoryBean;
    }
}

开发job实现类

/**
 * @author rikylinz
 * @description 自定义job
 * @date 2022/9/13 11:21
 */
@Component
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class CustomJob extends LoggerObject implements Job {

    @Autowired
    private ScheduleJobService scheduleJobService;//自定义的任务对象,后续有代码展示

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobDataMap map = jobExecutionContext.getMergedJobDataMap();
        String beanName = map.getString("beanName");
        String num = map.getString("num");
        //定时任务状态 改为执行中
        scheduleJobService.update(new UpdateWrapper<ScheduleJobPo>()
                .eq("num", num).set("status",1));
        //实际执行的内容
        int status;
        String failed = null;
        CommonResult commonResult = null;
        try {
            Timer timer = BeanUtils.getBean(beanName, Timer.class);
            commonResult = timer.timer(map);
            status = 3;
        } catch (Exception e) {
            logger.error("定时任务执行失败,num:{}", num,e);
            status = 4;
            failed = StringUtils.isEmpty(e.getMessage()) ? "定时任务执行失败" : e.getMessage();
        }
        //定时任务状态 改为执行完成
        UpdateWrapper<ScheduleJobPo> updateWrapper = new UpdateWrapper<ScheduleJobPo>()
                .eq("num", num).set("status", status);
        if(status == 3){
            updateWrapper.set("success_message", JSON.toJSONString(commonResult.getData()));
        }
        if(status == 4){
            updateWrapper.set("error_message",failed);
        }
        scheduleJobService.update(updateWrapper);
    }
}

开发任务类接口

/**
 * 自定义定时任务需要实现的接口
 * 注意:Map中必须带有num参数,该参数是执行任务对象的编号数据(该数据可以是任意类型的唯一性数据)
 */
public interface Timer {

    /**
     * 实现方式内容就是定时任务执行的内容
     * @param map
     * @return JSON数据
     */
    CommonResult timer(JobDataMap map);
}

开发任务列表枚举

@Getter
@AllArgsConstructor
public enum TimerEnum {

    DEMAND("customTimer","执行任务内容描述");

    private String beanName;//beanName
    private String message;//描述

}

开发定时任务配置类

/**
 * @author riKylinz
 * @description 定时任务配置类
 * @date 2022/9/13 13:45
 */
@Component
public class SchedulerConfig implements InitializingBean {

    @Autowired
    private SchedulerUse schedulerUse;//工具类

    @Override
    public void afterPropertiesSet() throws Exception {
        //启动定时任务
        schedulerUse.start();
    }

    @PostConstruct
    public void scheduleResume(){
        //项目启动时,清空定时器中的所有任务、恢复未执行以及执行中的任务
        schedulerUse.scheduleResume();
    }
}

开发定时任务使用工具类

/**
 * @author riKylinz
 * @description 定时任务使用类
 * @date 2022/9/13 13:06
 */
@Component
public class SchedulerUse extends LoggerObject {

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;
    @Autowired
    private ScheduleJobService scheduleJobService;

    /**
     * 开启定时任务-项目启动时执行
     */
    public void start(){
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        try {
            scheduler.start();
            logger.info("定时器启动成功!!!!");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 添加定时任务
     * @param jobDetail     jobDetail
     * @param trigger       触发器
     * @throws SchedulerException
     */
    public void scheduleJob(JobDetail jobDetail, Trigger trigger) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        scheduler.scheduleJob(jobDetail,trigger);
        logger.info("定时任务添加成功,jobName:{}",jobDetail.getKey().getName());
    }

    /**
     * 获取   JobDetail
     * @param scheduleJobPo 定时任务对象
     * @param jobDataMap    数据对象(在定时任务内容中使用的数据)
     * @param beanName      定时任务内容对象
     * @return  JobDetail
     */
    public JobDetail getJobDetail(ScheduleJobPo scheduleJobPo, JobDataMap jobDataMap, String beanName){
        return JobBuilder.newJob(CustomJob.class)
                .withIdentity(scheduleJobPo.getName(),scheduleJobPo.getGroupName())
                .usingJobData(jobDataMap)
                .usingJobData("beanName",beanName)
                .build();
    }

    /**
     * 获取 SimpleTrigger 触发器
     * @param scheduleJobPo  定时任务对象
     * @return  Trigger
     */
    public Trigger getSimpleTrigger(ScheduleJobPo scheduleJobPo){
        return TriggerBuilder.newTrigger()
                .withIdentity(scheduleJobPo.getName(),scheduleJobPo.getGroupName())
                .startAt(new Date(scheduleJobPo.getStartAt().getTime()))
                .build();
    }

    /**
     * 获取 CronTrigger 触发器
     * @param scheduleJobPo  定时任务对象
     * @return  Trigger
     */
    public Trigger getCronTrigger(ScheduleJobPo scheduleJobPo){
        return TriggerBuilder.newTrigger()
                .withIdentity(scheduleJobPo.getName(),scheduleJobPo.getGroupName())
                .withSchedule(CronScheduleBuilder.cronSchedule(scheduleJobPo.getCron()))
                .build();
    }

    /**
     * 删除定时器中所有任务
     */
    public void scheduleDeleteAll(){
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        try {
            Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.anyJobGroup());

            for (JobKey jobKey : jobKeys) {
                String name = jobKey.getName();
                String group = jobKey.getGroup();

                JobDetail jobDetail = scheduler.getJobDetail(jobKey);
                if(jobDetail == null){
                    return ;
                }

                scheduler.deleteJob(jobKey);

                //修改数据库
                scheduleJobService.update(new UpdateWrapper<ScheduleJobPo>().eq("name",name)
                        .eq("group_name",group).set("status",3));
            }
            //根据
        } catch (Exception e) {
            logger.error("删除之前存留定时任务失败,原因:{}",e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 恢复未执行的定时任务
     */
    public void scheduleResume(){
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            this.scheduleDeleteAll();

            //查询定时任务表  把未执行以及执行中的任务加入
            List<ScheduleJobPo> list = scheduleJobService.list(new QueryWrapper<ScheduleJobPo>()
                    .in("status", 0,1));

            for (ScheduleJobPo scheduleJobPo : list) {
                JobDataMap jobDataMap = JSONObject.parseObject(scheduleJobPo.getJobDataMap(), JobDataMap.class);
                JobDetail jobDetail = this.getJobDetail(scheduleJobPo, jobDataMap, scheduleJobPo.getTimerName());
                Trigger trigger;
                if(!StringUtils.isEmpty(scheduleJobPo.getCron())){
                    trigger = this.getCronTrigger(scheduleJobPo);
                }else{
                    trigger = this.getSimpleTrigger(scheduleJobPo);
                }
                logger.info("恢复定时任务!num:{}",scheduleJobPo.getNum());
                scheduler.scheduleJob(jobDetail,trigger);
            }
        } catch (Exception e) {
            logger.error("恢复未执行的定时任务失败,原因:{}",e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 暂停定时任务
     * @param jobKey    jobKey
     */
    public void schedulePause(JobKey jobKey) {
        try {
            String name = jobKey.getName();
            String group = jobKey.getGroup();
            ScheduleJobPo scheduleJobPo = scheduleJobService.getOne(new QueryWrapper<ScheduleJobPo>().eq("name", name)
                    .eq("group_name", group).in("status", 0, 1));
            if (scheduleJobPo == null){
                logger.info("定时任务不存在,jobKey:{}", JSON.toJSONString(jobKey));
                throw new RuntimeException("定时任务不存在");
            }

            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (jobDetail == null)
                return;
            scheduler.pauseJob(jobKey);
        }catch (Exception e){
           logger.error("暂停定时任务失败,jobKey:{},原因:{}",jobKey,e);
        }
    }
}

使用案例

  • 先实现timer接口,完成任务执行内容

  • 把实现类注入到spring容器中,并自定义bean名称

  • 把自定义bean名称配置到TimerEnum枚举中

  • 使用

    //scheduleJobPo需要提前组装好并存入数据库中
    JobDetail jobDetail = schedulerUse.getJobDetail(scheduleJobPo, jobDataMap, TimerEnum.DEMAND.getBeanName());
    Trigger simpleTrigger = schedulerUse.getSimpleTrigger(scheduleJobPo);
    try {
        schedulerUse.scheduleJob(jobDetail,simpleTrigger);
    } catch (SchedulerException e) {
        logger.error("定时任务添加失败,参数列表:{},原因:{}",JSON.toJSONString(demandPublishDto),e);
        throw new RuntimeException("定时任务添加失败");
    }
    

ScheduleJob实体类,实际根据自身项目而定

/**
 * @author riKylinz
 * @description 定时任务表
 * @date 2022/9/13 13:58
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@TableName("schedule_job")
public class ScheduleJobPo {

    @TableId(value = "id",type = IdType.AUTO)
    private Integer id;//主键id
    @TableField(value = "num")
    private String num;//编号,存储与其他表关联的唯一字段
    @TableField(value = "name")
    private String name;//定时任务 唯一标识
    @TableField(value = "group_name")
    private String groupName;//组名
    @TableField(value = "start_at")
    private Timestamp startAt;//执行时间
    @TableField(value = "cron")
    private String cron;//cron表达式
    @TableField(value = "timer_name")
    private String timerName;//任务实例名称
    @TableField(value = "job_data_map")
    private String jobDataMap;//参数map
    @TableField(value = "status")
    private Integer status;//状态 0:未执行 1:执行中 2:暂停 3:已完成 4:执行失败 -1:已删除
    @TableField(value = "success_message")
    private String successMessage;//执行成功信息
    @TableField(value = "error_message")
    private String errorMessage;//执行失败信息
    @TableField(value = "create_time")
    private Timestamp createTime;//创建时间
    @TableField(value = "update_time")
    private Timestamp updateTime;//修改时间

    public void setJobDataMap(String jobDataMap) {
        JobDataMap jobDataMap1 = JSONObject.parseObject(jobDataMap, JobDataMap.class);
        if(!jobDataMap1.containsKey("num")){
            throw new RuntimeException("num参数必填,数据任意类型,但唯一");
        }
        this.jobDataMap = jobDataMap;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值