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如何做分布式调度,对这些有兴趣的同学可以自行研究,后期我也会研究,并记录到博客中。