前言
公司之前的项目的定时任务都是使用@Schedule注解进行管理的;新需求需要实现对定时任务进行动态管理。后面决定用Quartz框架进行集成,以最小的代码来管理定时任务。
所需依赖:Springboot 1.xx 或 2.xx-RELEASE 都行, quartz 使用2.3.0版本
<dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.3.0</version></dependency>
为了简单演示,只创建一个任务类用于修改定时任务实体类即可,创建任务实体类:QuartzBean
@Data
public class QuartzBean implements Serializable {private static final long serialVersionUID=1L;@TableIdprivate String id;/*** 任务名称*/private String jobName;/*** 任务执行类*/private String jobClass;/*** 任务时间表达式*/@NotNullprivate String cron;/*** 任务启动状态 0 运行中 1 已关闭*/private Integer status;/*** 创建时间*/private Date createTime;
}
一、配置Quartz相关配置类
这里有三个配置类,分别为AutowiringSpringBeanJobFactory.java(主要是为了使得Job任务类运行时,可以注入其他service层)、QuartzConfig.java(quartz配置类,可设置对应的数据源,将schedule创建bean对象等)和 QuartzJobInitializer.java (用于项目启动时,可以从数据库里取出对应的QuartzBean对象,进行任务的自启动)
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;/*** 功能说明: job配置可注入其他服务类*/
public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {private transient AutowireCapableBeanFactory beanFactory;@Overridepublic void setApplicationContext(ApplicationContext context) {beanFactory = context.getAutowireCapableBeanFactory();}@Overrideprotected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {final Object job = super.createJobInstance(bundle);beanFactory.autowireBean(job);return job;}
}
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;/*** 功能说明: quartz配置类*/
@Configuration
public class QuartzConfig {/** 暂不使用数据库进行quartz的持久化@Beanpublic Properties properties() throws IOException {PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();// 对quartz.properties文件进行读取propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));// 在quartz.properties中的属性被读取并注入后再初始化对象propertiesFactoryBean.afterPropertiesSet();return propertiesFactoryBean.getObject();}**/@Beanpublic AutowiringSpringBeanJobFactory autowiringSpringBeanJobFactory() {return new AutowiringSpringBeanJobFactory();}@Beanpublic SchedulerFactoryBean schedulerFactoryBean() {SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();schedulerFactoryBean.setAutoStartup(true);schedulerFactoryBean.setJobFactory(autowiringSpringBeanJobFactory());// 从application.yml中读取Quartz的配置并设置到SchedulerFactoryBean中
// schedulerFactoryBean.setQuartzProperties(properties());return schedulerFactoryBean;}/*** 通过SchedulerFactoryBean获取Scheduler的实例* @param schedulerFactoryBean* @return*/@Beanpublic Scheduler scheduler(SchedulerFactoryBean schedulerFactoryBean) {return schedulerFactoryBean.getScheduler();}}
@Slf4j
@Component
public class QuartzJobInitializer {@Autowiredprivate Scheduler scheduler;@Autowiredprivate IQuartzBeanService quartzBeanService;@Autowiredpublic QuartzJobInitializer(Scheduler scheduler) {this.scheduler = scheduler;}@PostConstructpublic void init(){log.info("quartz:默认启动所有开启状态的定时任务");List<QuartzBean> quartzBeans = quartzBeanService.selectList(new EntityWrapper<>());List<QuartzBean> executeJobs = quartzBeans.stream().filter(f -> QuartzEnums.TaskStatusEnum.RUNNING.getType().equals(f.getStatus())).collect(Collectors.toList());try {// 这里将会启动所有定时任务if (CollUtil.isNotEmpty(executeJobs)) {executeJobs.forEach(item -> QuartzUtils.createScheduleJob(scheduler, item));}}catch (Exception e) {log.error("自启动定时任务异常: {}",e);}}}
二、创建QuartzUtils.java类
QuartzUtils类主要用于底层的创建、修改、删除、立即执行、暂停和恢复任务
mport com.bean.QuartzBean;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;/*** 功能说明: 定时任务执行工具类*/
@Slf4j
public class QuartzUtils {/*** 创建定时任务 定时任务创建之后默认启动状态* @param scheduler* @param quartzBean*/public static void createScheduleJob(Scheduler scheduler, QuartzBean quartzBean) {try {//获取到定时任务的执行类 必须是类的绝对路径名称//定时任务类需要是job类的具体实现 QuartzJobBean是job的抽象类。Class<? extends Job> jobClass = (Class<? extends Job>)Class.forName(quartzBean.getJobClass());// 构建定时任务信息JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(quartzBean.getJobName()).build();// 设置定时任务执行方式CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCron());// 构建触发器triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartzBean.getJobName()).withSchedule(scheduleBuilder).build();scheduler.scheduleJob(jobDetail, trigger);} catch (ClassNotFoundException e) {log.error("定时任务类路径出错:请输入类的绝对路径 {}",e.getMessage());} catch (SchedulerException e) {log.error("创建定时任务出错: {}", e.getMessage());}}/*** 根据任务名称暂停定时任务* @param scheduler* @param jobName*/public static void pauseScheduleJob(Scheduler scheduler, String jobName) {JobKey jobKey = JobKey.jobKey(jobName);try {scheduler.pauseJob(jobKey);} catch (SchedulerException e) {log.error("暂停定时任务出错: {}", e.getMessage());}}/*** 根据任务名称恢复定时任务* @param scheduler* @param jobName*/public static void resumeScheduleJob(Scheduler scheduler, String jobName) {JobKey jobKey = JobKey.jobKey(jobName);try {scheduler.resumeJob(jobKey);} catch (SchedulerException e) {System.out.println("恢复定时任务出错:"+e.getMessage());}}/*** 根据任务名称立即运行一次定时任务* @param scheduler* @param jobName*/public static void runOnce(Scheduler scheduler, String jobName){JobKey jobKey = JobKey.jobKey(jobName);try {scheduler.triggerJob(jobKey);} catch (SchedulerException e) {System.out.println("运行定时任务出错:"+e.getMessage());}}/*** 更新定时任务* @param scheduler* @param quartzBean*/public static void updateScheduleJob(Scheduler scheduler, QuartzBean quartzBean) {try {//获取到对应任务的触发器TriggerKey triggerKey = TriggerKey.triggerKey(quartzBean.getJobName());//设置定时任务执行方式CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCron());//重新构建任务的触发器triggerCronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();//重置对应的jobscheduler.rescheduleJob(triggerKey, trigger);} catch (SchedulerException e) {System.out.println("更新定时任务出错:"+e.getMessage());}}/*** 根据定时任务名称从调度器当中删除定时任务* @param scheduler* @param jobName*/public static void deleteScheduleJob(Scheduler scheduler, String jobName) {JobKey jobKey = JobKey.jobKey(jobName);try {scheduler.deleteJob(jobKey);} catch (SchedulerException e) {System.out.println("删除定时任务出错:"+e.getMessage());}}}
三、创建任务类,即定时任务可执行的类 MyQuartzTask
import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;/*** 功能说明: 定时任务-MyQuartzTask*/
@Slf4j
@Service
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class MyQuartzTask extends QuartzJobBean {@Overrideprotected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {String jobName = jobExecutionContext.getJobDetail().getKey().getName();log.info("定时任务【{}】启动成功", jobName);}
四、效果演示
@Slf4j
@RestController
@RequestMapping("/quartz")
public class QuartzController {@Autowiredprivate Scheduler scheduler;/*** 创建定时任务* @param quartzVO* @return*/@GetMapping("/createScheduleJob")public ResultVO createScheduleJob() {QuartzBean quartzBean = QuartzBean.builder().jobName("任务名称唯一").jobClass("com.service.impl.quartz.MyQuartzTask").cron("0 0/2 * * * ?").build();QuartzUtils.createScheduleJob(scheduler, quartzBean);return ResultVOUtil.success();}