欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringBoot整合quartz动态创建定时任务实现以及踩过的坑

程序员文章站 2022-03-29 19:25:20
...

需要了解一下quartz有Job,Scheduler,Trigger等概念,在此就不详细说明

首先说一下功能需求场景
该提醒可以在页面上进行配置多个,比如可以配置一个提醒:在每天的12点,发送邮件给某个人,可以继续配置另外一个提醒:在每个月的10号,给某个人转账,等等,多个动态的提醒,

说一下实现的方式
上面的需求即通过页面的配置,创建出来对应的定时任务,这些任务是动态创建出来的,不能够在代码中固定,该本章采用了quartz的Scheduler任务调度来实现动态任务的创建

实现功能路上踩过的坑
最开始的时候,对于这个动态创建定时任务我也觉得一脸懵逼,于是就在网上查找资料
最开始采用https://blog.csdn.net/upxiaofeng/article/details/79415108 这篇文章的思路进行编码,最开始测试是可以的,项目启动,成功创建出来对应的定时任务,但是在定时任务到达执行时间的时候,在执行继承Job接口的类中,发现通过Autowired注入进来的对象为null,通过查资料一时也没有解决,项目使用的springboot,网上的都是通过xml进行配置,于是就放弃了这篇文章的实现,之后又找到另一篇文章https://blog.csdn.net/lyg_come_on/article/details/78223344,一路照着敲完,发现在

StdScheduler stdScheduler = (StdScheduler) annotationContext.getBean(“mySchedulerFactoryBean”);//获得上面创建的bean

的时候报错了,原来是项目启动的时候,先进行创建定时任务,但是此刻并没有初始化MyJobFactory类中的bean,导致在创建scheduler时候并没有在spring上下文中找到这个bean,于是就打算写一个定时器,然后每天执行一次,执行的时候,调用动态创建定时任务的方法,刚好这样做,可以实现,在页面上修改任务提醒的配置,后台不用重新启动就可以动态更新任务,现在要实现就是对动态创建出来的任务进行修改定时时间,新增定时任务的操作了.

实现

1. InitRemindRuleScheduler
2. JobFactory
3. RemindTask
4. RemindRuleScheduler
5. TimingRemindTask
  1. InitRemindRuleScheduler 该本来用作创建mySchedulerFactoryBean的bean和调用RemindRuleScheduler的initStartJob来在项目启动的时候创建定时任务,无奈出现上面的问题,就只用来创建mySchedulerFactoryBean这个bean对象了
  2. JobFactory 用来创建job实例
  3. RemindTask创建的一个定时任务,每天会执行一遍,代替1中的onApplicationEvent方法
  4. RemindRuleScheduler从数据库查询定时提醒任务,进行更新任务.
  5. TimingRemindTask 任务的具体操作

说明一下,因为目前我的多个定期任务都是同一个任务逻辑操作,故只需要一个定时任务的业务逻辑实现,如果针对不同的任务,有不同的实现的话,在创建定时任务的时候,要针对生成这个任务的时候使用对应的类:TimingRemindTask(该类需实现Job接口)

JobDetail jobDetail = JobBuilder.newJob(TimingRemindTask.class)
                .withIdentity(remindRuleObjF04SQL01OM01.getId(), REMINDRULE )
                .usingJobData("remindRuleId",remindRuleObjF04SQL01OM01.getId())
                .build();

最后,贴上代码:
1. InitRemindRuleScheduler.class

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import com.paasit.pai.core.quartz.RemindRuleScheduler;

/**
 * @author 和彦鹏
 * 2018年9月15日
 */
@Configuration
public class InitRemindRuleScheduler implements ApplicationListener<ContextRefreshedEvent> {

    /**
     * 日志
     */
    private final Logger log = LoggerFactory.getLogger(InitRemindRuleScheduler.class);

    @Autowired
    private RemindRuleScheduler remindRuleScheduler;

    @Autowired
    private JobFactory jobFactory;

    @Bean(name ="mySchedulerFactoryBean")
    public SchedulerFactoryBean mySchedulerFactory() {
        SchedulerFactoryBean bean = new SchedulerFactoryBean();
        bean.setOverwriteExistingJobs(true);
        bean.setStartupDelay(1);
        bean.setJobFactory(jobFactory);
        return bean;
    }

    /**
     * 项目初始化的时候启动quartz
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        //log.debug("执行onApplicationEvent..");
        //try {
        //    remindRuleScheduler.initStartJob();
        //    log.debug("任务已经启动...");
        //} catch (SchedulerException e) {
        //    log.error("初始化启动错误:{}", e);
        //}
    }

}
  1. JobFactory
import org.quartz.spi.TriggerFiredBundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;

/**
 * @author 和彦鹏
 * 2018年9月16日
 */
@Component
public class JobFactory extends AdaptableJobFactory {

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

    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        // 调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        // 进行注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}
  1. RemindTask
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * @version: 0_1
 * @author: 和彦鹏
 * @date: 2018年6月6日11:37:44
 */
@Component
@Configurable
@EnableScheduling
public class RemindTask {

    /**
     * 日志
     */
    private final Logger log = LoggerFactory.getLogger(RemindTask.class);

    /**
     * 初始化动态创建定期任务
     */
    @Autowired
    private RemindRuleScheduler remindRuleScheduler;

    /**
     * 执行间隔提醒业务逻辑
     */
    @Autowired
    private IntervalRemindTask intervalRemindTask;

    // 每天执行定时任务:测试使用,每分钟执行
    @Scheduled(cron = "5 * * * * ?")
    public void testGetDemoData() {
        log.debug("RemindTask starting...");
        // A:创建或者更新定期的所有动态任务
        try {
            remindRuleScheduler.initStartJob();
        } catch (SchedulerException e) {
            log.error("RemindTask 初始化定期提醒任务失败.. {}", e);
        }
        log.debug("RemindTask end...");
    }
}
  1. RemindRuleScheduler
import java.text.MessageFormat;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.StdScheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import com.paasit.pai.core.dao.QueryDAO;
import com.paasit.pai.core.sql.dto.remindRuleObj.RemindRuleObjF04SQL01IM01;
import com.paasit.pai.core.sql.dto.remindRuleObj.RemindRuleObjF04SQL01OM01;
import com.paasit.pai.core.utils.SpringUtil;

/**
 * 描述: 提醒规则任务:
 * @author 和彦鹏
 * @date 2018年9月15日15:33:02
 * @version v0.0.1
 */
@Component
public class RemindRuleScheduler {

    /**
     * 日志
     */
    private final Logger log = LoggerFactory.getLogger(RemindRuleScheduler.class);

    private final String REMINDRULE = "REMINDRULE";

    @Autowired
    private QueryDAO queryDAO;


    /**
     * 开始执行所有任务
     * 
     * @throws SchedulerException
     */
    public void initStartJob() throws SchedulerException {
        // 从数据库中查询到所有的提醒
        ApplicationContext annotationContext = SpringUtil.getApplicationContext();
        StdScheduler stdScheduler = (StdScheduler) annotationContext.getBean("mySchedulerFactoryBean");//获得上面创建的bean
        Scheduler myScheduler = stdScheduler;
        RemindRuleObjF04SQL01IM01 remindRuleObjF04SQL01IM01 = new RemindRuleObjF04SQL01IM01();
        remindRuleObjF04SQL01IM01.setRemindType(0);// 0表示定期,该方法仅执行定期
        List<RemindRuleObjF04SQL01OM01> remindRuleObjF04SQL01OM01List = queryDAO.executeForObjectList("RemindRuleObjF04SQL01", remindRuleObjF04SQL01IM01);
        log.debug("RemindTask 数据库查询出来的定期任务数量:{}", remindRuleObjF04SQL01OM01List.size());
        if (remindRuleObjF04SQL01OM01List != null && remindRuleObjF04SQL01OM01List.size() > 0) {
            for (int i = 0; i < remindRuleObjF04SQL01OM01List.size(); i++) {
                startJob(myScheduler,remindRuleObjF04SQL01OM01List.get(i));
            }
            myScheduler.start();
        }
    }

    private void startJob(Scheduler myScheduler, RemindRuleObjF04SQL01OM01 remindRuleObjF04SQL01OM01) throws SchedulerException {
        // 创建触发器表达式{0}:表示小时,{1}:表示日, 
        String cron = MessageFormat.format("0 0 {0} {1} * ?", 
                remindRuleObjF04SQL01OM01.getOnHour() == null ? 0 : remindRuleObjF04SQL01OM01.getOnHour(),
                        remindRuleObjF04SQL01OM01.getOnDay() == null || remindRuleObjF04SQL01OM01.getOnDay() == 0 ? "*" : remindRuleObjF04SQL01OM01.getOnDay());
        log.info(MessageFormat.format("RemindTask 提醒任务Id=[{0}]的cron的表达式:[{1}]", remindRuleObjF04SQL01OM01.getId(), cron));

        TriggerKey triggerKey = new TriggerKey(remindRuleObjF04SQL01OM01.getId(), REMINDRULE);  
        CronTrigger cronTrigger = (CronTrigger) myScheduler.getTrigger(triggerKey);
        // 不存在这个任务,新增任务
        if (cronTrigger == null) {
            // 通过JobBuilder构建JobDetail实例,JobDetail规定只能是实现Job接口的实例
            JobDetail jobDetail = JobBuilder.newJob(TimingRemindTask.class)
                    .withIdentity(remindRuleObjF04SQL01OM01.getId(), REMINDRULE)
                    .usingJobData("remindRuleId",remindRuleObjF04SQL01OM01.getId())
                    .build();
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            // CronTrigger表达式触发器 继承于Trigger
            cronTrigger = TriggerBuilder.newTrigger().withIdentity(remindRuleObjF04SQL01OM01.getId(), REMINDRULE)
                    .withSchedule(cronScheduleBuilder).build();
            myScheduler.scheduleJob(jobDetail, cronTrigger);
            log.info("RemindTask 创建定时任务成功");
        } else {// 存在这个任务,判断这个任务的触发时间是否被修改过,如果修改过则更新任务
            String oldCron = cronTrigger.getCronExpression();
            // 新配置的cron和之前任务中使用的不一致,则更新
            if (!StringUtils.equalsIgnoreCase(cron, oldCron)) {
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
                CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(remindRuleObjF04SQL01OM01.getId(), REMINDRULE)
                        .withSchedule(cronScheduleBuilder).build();
                myScheduler.rescheduleJob(triggerKey, trigger);
                log.info("RemindTask 更新任务执行时间成功");
            } else {
                log.info("RemindTask 任务不进行操作");
            }
        } // TODO 暂没考虑页面上删除定期任务提醒
    }


//    /**
//     * 获取Job信息
//     */
//    public String getJobInfo(String name, String group) throws SchedulerException {
//        TriggerKey triggerKey = new TriggerKey(name, group);
//        CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
//        return String.format("time:%s,state:%s", cronTrigger.getCronExpression(),
//                scheduler.getTriggerState(triggerKey).name());
//    }
//    
//    /**
//     * 修改某个任务的执行时间
//     */
//    public boolean modifyJob(String name, String group, String time) throws SchedulerException {
//        Date date = null;
//        TriggerKey triggerKey = new TriggerKey(name, group);
//        CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
//        String oldTime = cronTrigger.getCronExpression();
//        if (!oldTime.equalsIgnoreCase(time)) {
//            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(time);
//            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(name, group)
//                    .withSchedule(cronScheduleBuilder).build();
//            date = scheduler.rescheduleJob(triggerKey, trigger);
//        }
//        return date != null;
//    }
//    
//    /**
//     * 暂停所有任务
//     */
//    public void pauseAllJob() throws SchedulerException {
//        scheduler.pauseAll();
//    }
//    
//    /**
//     * 暂停某个任务
//     */
//    public void pauseJob(String name, String group) throws SchedulerException {
//        JobKey jobKey = new JobKey(name, group);
//        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
//        if (jobDetail == null)
//            return;
//        scheduler.pauseJob(jobKey);
//    }
//
//    /**
//     * 恢复所有任务
//     */
//    public void resumeAllJob() throws SchedulerException {
//        scheduler.resumeAll();
//    }
//
//    /**
//     * 恢复某个任务
//     */
//    public void resumeJob(String name, String group) throws SchedulerException {
//        JobKey jobKey = new JobKey(name, group);
//        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
//        if (jobDetail == null)
//            return;
//        scheduler.resumeJob(jobKey);
//    }
//
//    /**
//     * 删除某个任务
//     */
//    public void deleteJob(String name, String group) throws SchedulerException {
//        JobKey jobKey = new JobKey(name, group);
//        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
//        if (jobDetail == null)
//            return;
//        scheduler.deleteJob(jobKey);
//    }

}
  1. TimingRemindTask
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.paasit.pai.core.dao.QueryDAO;
import com.paasit.pai.core.sql.dto.remindRuleObj.RemindRuleObjF04SQL01IM01;
import com.paasit.pai.core.sql.dto.remindRuleObj.RemindRuleObjF04SQL01OM01;

/**
 * 定时提醒的任务逻辑:该方法为动态创建出来的定时任务执行的逻辑,精确到remindRuleId,仅执行该条提醒的配置
 * @author 和彦鹏
 * @date 2018年9月16日15:33:09
 * @version v0.1
 */
@Component
public class TimingRemindTask implements Job {

    /**
     * 日志
     */
    private final Logger log = LoggerFactory.getLogger(TimingRemindTask.class);

    @Autowired
    private QueryDAO queryDAO;


    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        try {
            String remindRuleId = arg0.getJobDetail().getJobDataMap().getString("remindRuleId");
            log.debug("RemindTask 1.1执行定期提醒任务信息Id:{}", remindRuleId);
            RemindRuleObjF04SQL01IM01 remindRuleObjF04SQL01IM01 = new RemindRuleObjF04SQL01IM01();
            remindRuleObjF04SQL01IM01.setId(remindRuleId);
            RemindRuleObjF04SQL01OM01 remindRuleObjF04SQL01OM01 = queryDAO.executeForObject("RemindRuleObjF04SQL01", remindRuleObjF04SQL01IM01, RemindRuleObjF04SQL01OM01.class);
            // 进行业务逻辑处理
        } catch (Exception e) {
            log.error("RemindTask 1.9执行任务,获取任务信息Id出错,任务信息:{}", arg0);
        }
    }
}