SpringBoot集成Quartz,动态创建,更新,暂停,唤醒,删除任务调度

Quartz介绍

Quartz可以实现创建任务,修改任务周期等等功能,可以通过javaconfig配置任务调度,并在启动时执行,也可以动态创建任务。SpringBoot也集成了Quartz框架,并提供spring-boot-starter-quartz依赖。

Quartz中几个核心概念:

  • SchedulerFactoryBean:用来创建,配置一个Scheduler,并管理其生命周期。
  • Trigger:触发器,作用是任务何时执行。通过TriggerBuilder创建实例。
  • JobDetail:定义一个特定的Job,它通过JobBuilder来创建实例。

环境

SpringBoot版本:1.5.14.RELEASE

通过@Configuration静态配置Quartz

POM依赖

引入Quartz的相关依赖

                <!-- quartz -->
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.2.3</version>
		</dependency>
		<!-- spring集成quartz -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
		</dependency>
		<!-- 因为SchedulerFactoryBean中依赖了org.springframework.transaction.PlatformTransactionManager,所以需依赖tx相关包,其实还是quartz有个分布式功能,是使用数据库完成的。 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
		</dependency>

实现步骤

1.创建MethodInvokingJobDetailFactoryBean工厂类,设置目标对象和执行方法

2.通过第一步创建的工厂类创建触发器

3.创建任务调度工厂类,并注册触发器

编写如下配置类:

package com.bcu.springboot.biz.config;

import java.text.ParseException;
import java.util.Date;
import java.util.Locale;

import lombok.extern.slf4j.Slf4j;

import org.quartz.CronTrigger;
import org.quartz.Trigger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;


@Configuration
@Slf4j
public class QuartzConfig {
    
	/**
	 * 创建工厂bean,实现自定义调度类
	 * @return
	 */
	@Bean
	public MethodInvokingJobDetailFactoryBean createFactoryBean(){
		MethodInvokingJobDetailFactoryBean factoryBean = new MethodInvokingJobDetailFactoryBean();
		//设置执行对象
		factoryBean.setTargetBeanName("quartzTask");
		//设置执行方法
		factoryBean.setTargetMethod("quartzTask");
		//设置任务是否并发执行,true:任务并发执行,false:上一个任务执行完,下一个任务才开始执行
		factoryBean.setConcurrent(true);
		return factoryBean;
	}
	
	
	/**
	 * 通过工厂bean创建trigger触发器
	 * @param factoryBean
	 * @return
	 * @throws ParseException 
	 */
	@Bean
	public Trigger cronTrigger(MethodInvokingJobDetailFactoryBean factoryBean) throws ParseException{
		CronTriggerFactoryBean triggerBean = new CronTriggerFactoryBean();
		//设置trigger所属的工厂bean
		triggerBean.setJobDetail(factoryBean.getObject());
		//设置执行周期
		triggerBean.setCronExpression("0/3 * * * * ?");
		triggerBean.setName("customTrigger");
		//在bean属性填充完成后,执行初始化,如果配置出错则抛出异常
		triggerBean.afterPropertiesSet();
		return triggerBean.getObject();
	}
	
	/**
	 * 创建调度工厂类,自动注册trigger
	 * @param triggers
	 * @return
	 */
	@Bean
	public SchedulerFactoryBean schedulerFactoryBean(CronTrigger... triggers){
		SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
		//当triggers为null时,报异常
		factoryBean.setTriggers(triggers);
		return factoryBean;
	}

	@Component("quartzTask")
	public class QuartzTask{
		public void quartzTask() {
			DateFormatter df = new DateFormatter("yyyyMMdd HH:mm:ss");
			System.out.println("["+Thread.currentThread().getName()+"]执行任务,当前时间:"+df.print(new Date(), Locale.CHINA));
		}
	}
	
	
}

动态创建Quartz

首先,创建一个Job工厂,用于将Job实例注入到Job工厂中

package com.bcu.springboot.biz.quartz;

import org.quartz.spi.TriggerFiredBundle;
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 hww
 *	Job工厂
 */
@Component
public class JobFactory extends AdaptableJobFactory{

	@Autowired
	private AutowireCapableBeanFactory beanFactory;
	
	@Override
	protected Object createJobInstance(TriggerFiredBundle bundle)
			throws Exception {
		Object jobInstance = super.createJobInstance(bundle);
		//将Job实例注入到Job工厂
		beanFactory.autowireBean(jobInstance);
		return jobInstance;
	}
	
}

其次,创建配置类用于创建SchedulerFactoryBean实例,以及获取Scheduler

package com.bcu.springboot.biz.quartz;

import java.io.IOException;
import java.text.ParseException;
import java.util.Date;
import java.util.Locale;
import java.util.Properties;

import lombok.extern.slf4j.Slf4j;

import org.quartz.CronTrigger;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.ee.servlet.QuartzInitializerListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;

/**
 * @author hww
 */
@Configuration
@Slf4j
public class QuartzConfig {
    
	@Autowired
	private JobFactory jobFactory;
	
	/**
	 * 调度类FactoyBean
	 * @return
	 * @throws IOException
	 */
	@Bean("sechduerFactory")
	public SchedulerFactoryBean schedulerFactoryBean() throws IOException{
		SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
		//设置调度类quartz属性
		schedulerFactoryBean.setQuartzProperties(quartzProperties());
		//设置jobFactory
		schedulerFactoryBean.setJobFactory(jobFactory);
		return schedulerFactoryBean;
	}
	
	/**
	 * 解析quartz.properties文件,填充属性
	 * @return
	 * @throws IOException
	 */
	@Bean
	public Properties quartzProperties() throws IOException{
		PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
//		propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
		propertiesFactoryBean.afterPropertiesSet();
		return propertiesFactoryBean.getObject();
	}
	
	/**
	 * quartz初始化监听器
	 * @return
	 */
	@Bean
	public QuartzInitializerListener initializerListener(){
		return new QuartzInitializerListener();
	}
	
	/**
	 * 根据调度类工厂bean获取调度
	 * @return
	 * @throws IOException 
	 */
	@Bean("scheduler")
	public Scheduler scheduler() throws IOException{
		return schedulerFactoryBean().getScheduler();
	}
	
}

此时,我们已经可以通过@Autowired("schrduler")来获取到Scheduler了,然后,创建一个Job实体类,用于存放Job的各种信息(set,get方法省略)

package com.bcu.springboot.biz.quartz;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

import java.util.Date;
 
@ApiModel
public class SchedulerJob {
	
    @ApiModelProperty(value="jobid",dataType="String",name="jobid",example="1")
    private String jobid;
 
    @ApiModelProperty(value="classname",dataType="String",name="classname",example="com.bcu.springboot.biz.quartz.CustomJob")
    private String classname;
 
    @ApiModelProperty(value="cronexpression",dataType="String",name="cronexpression",example="0/5 * * * * ?")
    private String cronexpression;
 
    @ApiModelProperty(value="jobname",dataType="String",name="jobname",example="job1")
    private String jobname;
 
    @ApiModelProperty(value="jobgroup",dataType="String",name="jobgroup",example="group1")
    private String jobgroup;
 
    @ApiModelProperty(value="triggername",dataType="String",name="triggername",example="triggername1")
    private String triggername;
 
    @ApiModelProperty(value="triggergroup",dataType="String",name="triggergroup",example="triggergroup1")
    private String triggergroup;
 
    @ApiModelProperty(value="pause",dataType="Boolean",name="pause",example="true")
    private Boolean pause;
 
    private Boolean enable;
 
    @ApiModelProperty(value="description",dataType="String",name="description",example="秒杀活动")
    private String description;
 
    private Date createtime;
 
    private Date lastupdatetime;
 
    private String owner;
}

编写QuartzService接口,定义需要实现的功能

package com.bcu.springboot.biz.quartz;

/**
 * Quartz任务接口
 * @author hww
 *	
 */
public interface QuartzService {

	/**
	 * 创建Job
	 * @param job
	 */
	public void addTimerJob(SchedulerJob job);
	
	/**
	 * 执行Job
	 * @param job
	 */
	public void runTimerJob(SchedulerJob job);
	
	/**
	 * 修改Job
	 * @param job
	 */
	public void updateTimerJob(SchedulerJob job);
	
	/**
	 * 暂定Job
	 * @param job
	 */
	public void pauseTimerJob(SchedulerJob job);
	
	/**
	 * 唤醒Job
	 * @param job
	 */
	public void resumeTimerJob(SchedulerJob job);
	
	/**
	 * 删除Job
	 * @param job
	 */
	public void deleteTimerJob(SchedulerJob job);
	
	/**
	 * 获取Job
	 * @param job
	 */
	public String selectTimerJob(SchedulerJob job);
	
}

编写QuartzService接口实现类

package com.bcu.springboot.biz.quartz;

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;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

/**
 * Quartz任务实现类
 * 
 * @author hww
 * 
 */
@Service
public class QuartzServiceImpl implements QuartzService {

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

	@SuppressWarnings("unchecked")
	@Override
	public void addTimerJob(SchedulerJob job) {
		try {
			JobDetail jobDetail = JobBuilder
					.newJob((Class<? extends Job>) Class.forName(job.getClassname()))
					// 指定执行类
					.withIdentity(job.getJobname(), job.getJobgroup())
					// 指定name和group
					.requestRecovery().withDescription(job.getDescription())
					.build();
			// 创建表达式调度构建器
			CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder
					.cronSchedule(job.getCronexpression());
			// 创建触发器
			CronTrigger cronTrigger = TriggerBuilder.newTrigger()
					.withIdentity(job.getTriggername(), job.getTriggergroup())
					.withSchedule(cronScheduleBuilder).build();
			scheduler.scheduleJob(jobDetail, cronTrigger);
			scheduler.start();// 触发器并不会立刻触发
			System.out
					.println("==================================创建Job成功!==================================");
		} catch (ClassNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (SchedulerException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void runTimerJob(SchedulerJob job) {
		try {
			scheduler.triggerJob(JobKey.jobKey(job.getJobname(),
					job.getJobgroup()));
			System.out
					.println("==================================Job执行成功!==================================");
		} catch (SchedulerException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void updateTimerJob(SchedulerJob job) {
		try {
			TriggerKey triggerKey = new TriggerKey(job.getTriggername(),
					job.getTriggergroup());
			CronTrigger cronTrigger = (CronTrigger) scheduler
					.getTrigger(triggerKey);
			CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder
					.cronSchedule(job.getCronexpression());
			// 重新构件表达式
			CronTrigger trigger = cronTrigger.getTriggerBuilder()
					.withIdentity(triggerKey).withSchedule(cronScheduleBuilder)
					.build();
			scheduler.rescheduleJob(triggerKey, trigger);
			System.out
					.println("==================================更新Job成功!==================================");
		} catch (SchedulerException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void pauseTimerJob(SchedulerJob job) {
		try {
			scheduler.pauseJob(JobKey.jobKey(job.getJobname(),
					job.getJobgroup()));
			System.out
					.println("==================================暂停Job!==================================");
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void resumeTimerJob(SchedulerJob job) {
		try {
			scheduler.resumeJob(JobKey.jobKey(job.getJobname(),
					job.getJobgroup()));
			System.out
					.println("==================================重启Job!==================================");
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void deleteTimerJob(SchedulerJob job) {
		try {
			scheduler.deleteJob(JobKey.jobKey(job.getJobname(),
					job.getJobgroup()));
			System.out
					.println("==================================删除Job!==================================");
		} catch (SchedulerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public String selectTimerJob(SchedulerJob job) {
		TriggerKey triggerKey = new TriggerKey(job.getTriggername(),
				job.getTriggergroup());
		try {
			CronTrigger cronTrigger = (CronTrigger) scheduler
					.getTrigger(triggerKey);
			return "expression:" + cronTrigger.getCronExpression()
					+ ",description:" + cronTrigger.getDescription()
					+ ",state:" + scheduler.getTriggerState(triggerKey);
		} catch (SchedulerException e) {
			e.printStackTrace();
		}
		return null;
	}

}

至此,我们已经实现了动态创建任务的功能,并且还实现了动态修改任务周期,暂停任务,唤醒任务,删除任务以及查询任务执行状态等信息。我们只需要封装并传入一个SchedulerJob实体类(存放Job的相关信息)即可。

接下来,编写需要调度的执行类,这也是我们日常开发业务代码的位置

package com.bcu.springboot.biz.quartz;

import java.util.Date;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;

@Component
public class CustomJob implements Job {

	private void before() {
		System.out.println("开始执行");
	}

	@Override
	public void execute(JobExecutionContext context)
			throws JobExecutionException {
		before();
		//业务逻辑
		
		//获取job id
		String name = context.getJobDetail().getKey().getName();
		System.out.println("["+Thread.currentThread().getName()+"],执行Job"+name+",当前时间:"+new Date());
		after();
	}

	private void after() {
		System.out.println("执行结束");
	}

}

接下来编写一个Controller类

package com.bcu.springboot.biz.controller;

import junit.framework.Assert;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.bcu.springboot.biz.quartz.QuartzService;
import com.bcu.springboot.biz.quartz.SchedulerJob;

@SuppressWarnings("deprecation")
@RestController
@RequestMapping("/quartz")
@Api(tags="Quartz测试")
public class QuartzController {

	@Autowired
	private QuartzService quartzService;
	
	@RequestMapping("/add")
	@ApiOperation("创建Quartz任务")
	public String addJob(@RequestBody SchedulerJob job) throws Exception{
		if(job==null) throw new Exception("job is null");
		quartzService.addTimerJob(job);
		return "创建Quartz任务成功";
	}
	
	@RequestMapping("/run")
	@ApiOperation("启动Quartz任务")
	public String runJob(@RequestBody SchedulerJob job) throws Exception{
		if(job==null) throw new Exception("job is null");
		quartzService.runTimerJob(job);
		return "启动Quartz任务成功";
	}
	
	@RequestMapping("/update")
	@ApiOperation("修改Quartz任务")
	public String updateJob(@RequestBody SchedulerJob job) throws Exception{
		if(job==null) throw new Exception("job is null");
		quartzService.updateTimerJob(job);
		return "修改Quartz任务成功";
	}
	
	@RequestMapping("/pause")
	@ApiOperation("暂停Quartz任务")
	public String pauseJob(@RequestBody SchedulerJob job) throws Exception{
		if(job==null) throw new Exception("job is null");
		quartzService.pauseTimerJob(job);
		return "暂停Quartz任务成功";
	}
	
	@RequestMapping("/resume")
	@ApiOperation("重启Quartz任务")
	public String resumeJob(@RequestBody SchedulerJob job) throws Exception{
		if(job==null) throw new Exception("job is null");
		quartzService.resumeTimerJob(job);
		return "重启Quartz任务成功";
	}
	
	@RequestMapping("/delete")
	@ApiOperation("删除Quartz任务")
	public String deleteJob(@RequestBody SchedulerJob job) throws Exception{
		if(job==null) throw new Exception("job is null");
		quartzService.deleteTimerJob(job);
		return "删除Quartz任务成功";
	}
	
	@RequestMapping("/select")
	@ApiOperation("获取Quartz任务")
	public String selectJob(@RequestBody SchedulerJob job) throws Exception{
		if(job==null) throw new Exception("job is null");
		String selectTimerJob = quartzService.selectTimerJob(job);
		return selectTimerJob;
	}
	
}

最后用 postman测试了一下,以下是部分截图

总结:一般情况下,静态配置任务调度适用于项目启动时就开始执行的任务,比如自动更新静态数据等。而动态创建任务则适用于定时发送信息,定时生成报表等任务。这里只是讲解了Quartz的基本使用,而像Quartz结合redis自动更新页面静态数据,Quartz如何做分布式调度,对这些有兴趣的同学可以自行研究,后期我也会研究,并记录到博客中。

猜你喜欢

转载自blog.csdn.net/ZixiangLi/article/details/88826019