Springboot使用Quartz框架(三)--Quartz在项目中的使用

假装没事ソ 提交于 2019-12-10 09:49:15

前面2篇文章介绍了quartz简单的使用以及quartz核心类的作用,这一篇文章主要介绍quartz在项目中的使用

1.自定义注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JobUnit {
    /**
     * @return
     */
    String jobName() default "";

    String jobGroup() default "";

    String jobDesc() default "";

    String jobCorn() default "";

    int misfireInstruction() default CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING;

    /**
     * 是否单例,单例情况下以代码配置为主,系统无法修改 jobName+jobGroup,防止生成多个同任务定时器
     *
     * @return
     */
    boolean singleton() default true;
}
2.任务配置单元数据
public class QuartzJobUnit implements Serializable {

    private static final long serialVersionUID = 7669523527816564621L;

    /**
     * 任务名称
     */
    private String jobName;
    /**
     * 任务分组
     */
    private String jobGroup;
    /**
     * 任务表达式
     */
    private String jobCorn;
    /**
     * 任务描述
     */
    private String description;
    /**
     * 存活时间,单位秒
     */
    private long surviveSecond;
    /**
     * 扩展单元数据
     */
    private Map<String, Object> extraUnit;

    /**
     * 丢失补仓策略
     */
    private int misfireInstruction = CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING;
    
	//省略get、set
 }

3.定义Job任务

@JobUnit(jobName = "QuartzJob1", jobGroup = "QuartzJob", jobCorn = "*/5 * * * * ?", jobDesc = "Quartz学习")
public class QuartzJob1 implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobKey key = jobExecutionContext.getJobDetail().getKey();
        JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
        String jobMsg = jobDataMap.getString("jobMsg");
        String triggerMsg = jobDataMap.getString("triggerMsg");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(jobMsg + "=====" + triggerMsg + sdf.format(new Date()));
    }
}

4.定义调度监听器

public class QuartzCustomSchedulerListener implements SchedulerListener {

    private static final Logger LOG = LoggerFactory.getLogger(QuartzCustomSchedulerListener.class);

    /**
     * 任务被部署时被执行
     * @param trigger
     */
    @Override
    public void jobScheduled(Trigger trigger) {
        LOG.info("任务 [{}] 被部署", trigger.toString());
    }

    /**
     * 任务被卸载时被执行
     * @param triggerKey
     */
    @Override
    public void jobUnscheduled(TriggerKey triggerKey) {
        LOG.info("任务 [{}] 被卸载", triggerKey.toString());
    }

    /**
     * 任务完成了它的使命,光荣退休时被执行
     * @param trigger
     */
    @Override
    public void triggerFinalized(Trigger trigger) {
        LOG.info("任务 [{}] 结束", trigger.toString());
    }

    /**
     * 一个触发器被暂停时被执行
     * @param triggerKey
     */
    @Override
    public void triggerPaused(TriggerKey triggerKey) {
        LOG.info("任务 [{}] 被暂停", triggerKey.toString());
    }

    /**
     * 所在组的全部触发器被停止时被执行
     * @param triggerGroup
     */
    @Override
    public void triggersPaused(String triggerGroup) {
        LOG.info("任务组 [{}] 被暂停", triggerGroup);

    }

    /**
     * 一个触发器被恢复时被执行
     * @param triggerKey
     */
    @Override
    public void triggerResumed(TriggerKey triggerKey) {
        LOG.info("任务 [{}] 被恢复", triggerKey.toString());
    }

    /**
     * 所在组的全部触发器被回复时被执行
     * @param triggerGroup
     */
    @Override
    public void triggersResumed(String triggerGroup) {
        LOG.info("任务组 [{}] 被恢复", triggerGroup);
    }

    /**
     * 一个JobDetail被动态添加进来
     * @param jobDetail
     */
    @Override
    public void jobAdded(JobDetail jobDetail) {

    }

    /**
     * 删除时被执行
     * @param jobKey
     */
    @Override
    public void jobDeleted(JobKey jobKey) {
        LOG.info("任务 [{}] 被删除", jobKey.toString());
    }

    /**
     * 暂停时被执行
     * @param jobKey
     */
    @Override
    public void jobPaused(JobKey jobKey) {
        LOG.info("任务 [{}] 被暂停", jobKey.toString());
    }

    /**
     * 一组任务被暂定时执行
     * @param jobGroup
     */
    @Override
    public void jobsPaused(String jobGroup) {
        LOG.info("任务组 [{}] 被暂停", jobGroup);
    }

    /**
     * 恢复时被执行
     * @param jobKey
     */
    @Override
    public void jobResumed(JobKey jobKey) {
        LOG.info("任务 [{}] 被恢复", jobKey.toString());
    }

    /**
     * 一组被恢复时执行
     * @param triggerGroup
     */
    @Override
    public void jobsResumed(String triggerGroup) {
        LOG.info("任务组 [{}] 被恢复", triggerGroup);
    }

    /**
     * 出现异常时执行
     * @param jobGroup
     * @param e
     */
    @Override
    public void schedulerError(String jobGroup, SchedulerException e) {
        LOG.error("jobGroup [{}] deal error: {}", jobGroup, getStackTraceMsg(e.getStackTrace()));
    }

    /**
     * scheduler被设为standBy等候模式时被执行
     */
    @Override
    public void schedulerInStandbyMode() {

    }

    /**
     * scheduler启动时被执行
     */
    @Override
    public void schedulerStarted() {

    }

    /**
     * scheduler正在启动时被执行
     */
    @Override
    public void schedulerStarting() {

    }

    /**
     * scheduler关闭时被执行
     */
    @Override
    public void schedulerShutdown() {

    }

    /**
     * scheduler正在关闭时被执行
     */
    @Override
    public void schedulerShuttingdown() {

    }

    /**
     * scheduler中所有数据包括jobs, triggers和calendars都被清空时被执行
     */
    @Override
    public void schedulingDataCleared() {

    }

    /**
     * 日志栈输出
     *
     * @param stackTraceElements
     * @return
     */
    public String getStackTraceMsg(StackTraceElement[] stackTraceElements) {
        StringBuilder sb = new StringBuilder();
        for (StackTraceElement stackTraceElem : stackTraceElements) {
            sb.append(stackTraceElem.toString() + "\n");
        }
        return sb.toString();
    }
}

5.定义任务监听器

public class JobMonitorListener implements JobListener {

    private static final ThreadLocal<Long> TIME_THREADLOCAL = new ThreadLocal<Long>() {
        @Override
        protected Long initialValue() {
            return System.currentTimeMillis();
        }
    };

    private static final Logger LOGGER = LoggerFactory.getLogger(JobMonitorListener.class);

    @Override
    public String getName() {
        return "JobListenerName";
    }

    /**
     * 定时任务开始执行
     *
     * @param context
     */
    @Override
    public void jobToBeExecuted(JobExecutionContext context) {
        String jobName = context.getJobDetail().getKey().toString();
        long startTime = System.currentTimeMillis();
        TIME_THREADLOCAL.set(System.currentTimeMillis());
        String executeTime = DateFormatUtils.format(new Date(startTime), "yyyy-MM-dd HH:mm:ss");
        LOGGER.info("Job [{}] is going to start! BeginTime : [{}]", jobName, executeTime);
    }

    /**
     * 定时任务被否决
     *
     * @param context
     */
    @Override
    public void jobExecutionVetoed(JobExecutionContext context) {
        String executeTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        String jobName = context.getJobDetail().getKey().toString();
        LOGGER.warn("Job [{}] is vetoed, DateTime: {}", jobName, executeTime);
    }

    /**
     * 定时任务执行完毕
     *
     * @param context
     * @param jobException
     */
    @Override
    public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
        long endTime = System.currentTimeMillis();
        long costTime = System.currentTimeMillis() - TIME_THREADLOCAL.get();
        String jobName = context.getJobDetail().getKey().toString();
        String executeTime = DateFormatUtils.format(new Date(endTime), "yyyy-MM-dd HH:mm:ss");
        LOGGER.info("Job [{}] is finished! EndTime: [{}] , Cost time [{}] ms", jobName, executeTime, costTime);
        if (jobException != null && !jobException.getMessage().equals("")) {
            LOGGER.error("job [{}] is execute dateTime [{}] error: {}", jobName, executeTime, getStackTraceMsg(jobException.getStackTrace()));
        }
    }

    /**
     * 日志栈输出
     *
     * @param stackTraceElements
     * @return
     */
    public String getStackTraceMsg(StackTraceElement[] stackTraceElements) {
        StringBuilder sb = new StringBuilder();
        for (StackTraceElement stackTraceElem : stackTraceElements) {
            sb.append(stackTraceElem.toString() + "\n");
        }
        return sb.toString();
    }
}

6.项目启动时初始化Scheduler

@Configuration
public class QuartzConfiguration {
    @Bean(name = "scheduler")
    public Scheduler scheduler() throws Exception {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.getListenerManager().addSchedulerListener(new QuartzCustomSchedulerListener());
        scheduler.getListenerManager().addJobListener(new JobMonitorListener());
        return scheduler;
    }
}

7.QuartzJobManager管理类

@Service
@AutoConfigureAfter(value = {QuartzConfiguration.class})
public class QuartzJobManager {

    private static final Logger LOG = LoggerFactory.getLogger(QuartzJobManager.class);

    @Qualifier(value = "scheduler")
    @Autowired
    private Scheduler scheduler;


    /**
     * 添加任务</br>
     * 同一个任务只能添加一次,时间表达式不一样时会更新定时任务
     *
     * @param jobUnit
     * @param jobClass
     */
    public void addJob(QuartzJobUnit jobUnit, @SuppressWarnings("rawtypes") Class jobClass) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobUnit.getJobName(), jobUnit.getJobGroup());
            if (!scheduler.checkExists(triggerKey)) {
                JobDetail jobDetail = null;
                if (StringUtils.isNotBlank(jobUnit.getDescription())) {
                    jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobUnit.getJobName(), jobUnit.getJobGroup()).withDescription(jobUnit.getDescription()).build();
                } else {
                    jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobUnit.getJobName(), jobUnit.getJobGroup()).build();
                }
                jobDetail.getJobDataMap().put("scheduleJob", jobUnit);
                //表达式调度构建器
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobUnit.getJobCorn());
                CronTriggerImpl trigger = (CronTriggerImpl) TriggerBuilder.newTrigger().withIdentity(jobUnit.getJobName(), jobUnit.getJobGroup()).withSchedule(scheduleBuilder).build();
                if (StringUtils.isNotBlank(jobUnit.getDescription())) {
                    trigger.setDescription(jobUnit.getDescription());
                }
                if (jobUnit.getSurviveSecond() > 0L) {
                    trigger.setEndTime(new Date(System.currentTimeMillis() + jobUnit.getSurviveSecond() * 1000));
                }
                trigger.setMisfireInstruction(jobUnit.getMisfireInstruction());
                trigger.setJobDataMap(jobDetail.getJobDataMap());
                // 注册job和调度器
                scheduler.scheduleJob(jobDetail, trigger);
                scheduler.start();
//                LOG.info("add quartz job [{}] success!", triggerKey.toString());
            } else {
                CronTriggerImpl trigger = (CronTriggerImpl) scheduler.getTrigger(triggerKey);
                if (!trigger.getCronExpression().equalsIgnoreCase(jobUnit.getJobCorn()) || (StringUtils.isNotBlank(jobUnit.getDescription()) && !jobUnit.getDescription().equals(trigger.getDescription()))) {
                    CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobUnit.getJobCorn());
                    trigger = (CronTriggerImpl) trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
                    if (jobUnit.getSurviveSecond() > 0L) {
                        trigger.setEndTime(new Date(System.currentTimeMillis() + jobUnit.getSurviveSecond() * 1000));
                    }
                    if (StringUtils.isNotBlank(jobUnit.getDescription())) {
                        trigger.setDescription(jobUnit.getDescription());
                    }
                    scheduler.rescheduleJob(triggerKey, trigger);
//                    LOG.info("update quartz job [{}] success!", triggerKey.toString());
                }
            }
        } catch (SchedulerException e) {
            LOG.error("add quartz job error: {}", e);
        }
    }


    /**
     * 移除任务
     *
     * @param jobUnit
     */
    public void removeJob(QuartzJobUnit jobUnit) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobUnit.getJobName(), jobUnit.getJobGroup());
            scheduler.pauseTrigger(triggerKey);// 停止触发器
            scheduler.unscheduleJob(triggerKey);// 移除触发器
            JobKey jobKey = JobKey.jobKey(jobUnit.getJobName(), jobUnit.getJobGroup());
            scheduler.deleteJob(jobKey);// 删除任务
        } catch (SchedulerException e) {
            //throw new RuntimeException(e);
        }
    }

    /**
     * 停止任务
     *
     * @param jobUnit
     */
    public void pauseJob(QuartzJobUnit jobUnit) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobUnit.getJobName(), jobUnit.getJobGroup());
            scheduler.pauseTrigger(triggerKey);// 停止触发器
        } catch (SchedulerException e) {
            LOG.error("暂定任务失败:", e);
        }
    }

    /**
     * 恢复任务
     *
     * @param jobUnit
     */
    public void resumeJob(QuartzJobUnit jobUnit) {
        try {
            JobKey jobKey = JobKey.jobKey(jobUnit.getJobName(), jobUnit.getJobGroup());
            scheduler.resumeJob(jobKey);// 停止触发器
        } catch (SchedulerException e) {
            //throw new RuntimeException(e);
        }
    }

    /**
     * 开始所有任务
     */
    public void startJobs() {
        try {
            scheduler.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 停止所有任务
     */
    public void shutdownJobs() {
        try {
            if (!scheduler.isShutdown()) {
                scheduler.shutdown();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 立即执行任务
     *
     * @param jobUnit
     */
    public void triggerJob(QuartzJobUnit jobUnit) {
        try {
            JobKey jobKey = JobKey.jobKey(jobUnit.getJobName(), jobUnit.getJobGroup());
            scheduler.triggerJob(jobKey);
        } catch (Exception e) {

        }

    }
}

8.启动类中注册定时任务

@SpringBootApplication
public class ToaliApplication implements CommandLineRunner {

    private static final Logger LOGGER = LoggerFactory.getLogger(ToaliApplication.class);

    private static final String QUARTZ_JOB_PKG = "com.haidong.yktui.quartz.job";

    @Autowired
    private QuartzJobManager quartzJobManager;

    public static void main(String[] args) {
        SpringApplication.run(ToaliApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        registerQuartzJob();
    }

    /**
     * 注册定时任务
     *
     * @throws Exception
     */
    private void registerQuartzJob() throws Exception {
        LOGGER.info("start to register quartz job...");
        final ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(JobUnit.class));
        for (final BeanDefinition resourceBean : scanner.findCandidateComponents(QUARTZ_JOB_PKG)) {
            try {
                final Class resourceClass = getClass().getClassLoader().loadClass(resourceBean.getBeanClassName());
                if (null != resourceClass) {
                    JobUnit jobUnit = (JobUnit) resourceClass.getAnnotation(JobUnit.class);
                    String jobName = jobUnit.jobName();
                    if (StringUtils.isBlank(jobName)) {
                        jobName = resourceClass.getSimpleName();
                    }
                    QuartzJobUnit quartzJobUnit = new QuartzJobUnit();
                    quartzJobUnit.setJobName(jobName);
                    quartzJobUnit.setJobGroup(jobUnit.jobGroup());
                    quartzJobUnit.setJobCorn(jobUnit.jobCorn());
                    quartzJobUnit.setMisfireInstruction(jobUnit.misfireInstruction());
                    if (StringUtils.isNotBlank(jobUnit.jobDesc())) {
                        quartzJobUnit.setDescription(jobUnit.jobDesc());
                    }
                    quartzJobManager.addJob(quartzJobUnit, resourceClass);
                }
            } catch (final ClassNotFoundException e) {
                LOGGER.error("error to register quartz job bean [{}]", resourceBean.getBeanClassName(), e);
            }
        }
        LOGGER.info("finished register quartz job...");
    }

}

9.当我们运行后发现报错了:

这个因为job任务是交给quartz处理的,而job任务中引入的TestAutowired是spring管理的,他们是2个东西,所以引入的TestAutowired是空。具体原因见第四章

2019-12-10 00:55:00,106 ERROR (JobRunShell.java:211)- Job QuartzJob.QuartzJob1 threw an unhandled Exception: 
java.lang.NullPointerException: null
	at com.yhx.toali.Quartz.QuartzFrame.QuartzJob1.execute(QuartzJob1.java:33)
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
2019-12-10 00:55:00,107 ERROR (QuartzCustomSchedulerListener.java:157)- jobGroup [Job (QuartzJob.QuartzJob1 threw an exception.] deal error: org.quartz.core.JobRunShell.run(JobRunShell.java:213)
org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)

10.添加JobFactory

@Component(value = "quartzCustomAdaptableJobFactory")
public class QuartzCustomAdaptableJobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //实例化对象
        Object jobInstance = super.createJobInstance(bundle);
        //进行注入,交给spring管理该bean
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

11.修改Scheduler的获取方式

@Configuration
public class QuartzConfiguration {
    /*@Bean(name = "scheduler")
    public Scheduler scheduler() throws Exception {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.getListenerManager().addSchedulerListener(new QuartzCustomSchedulerListener());
        scheduler.getListenerManager().addJobListener(new JobMonitorListener());
        return scheduler;
    }*/

    @Bean(value = "schedulerFactory")
    public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("quartzCustomAdaptableJobFactory") QuartzCustomAdaptableJobFactory quartzCustomAdaptableJobFactory) {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(quartzCustomAdaptableJobFactory);

        return schedulerFactoryBean;
    }

    @Bean(name = "scheduler")
    public Scheduler scheduler(@Qualifier("schedulerFactory") SchedulerFactoryBean schedulerFactory) throws Exception {
        Scheduler scheduler = schedulerFactory.getScheduler();
        scheduler.getListenerManager().addSchedulerListener(new QuartzCustomSchedulerListener());
        scheduler.getListenerManager().addJobListener(new JobMonitorListener());
        return scheduler;
    }
}

12.注释掉QuartzJobManager中的调度器的启动

 // 注册job和调度器
 scheduler.scheduleJob(jobDetail, trigger);
 // scheduler.start();
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!