本文仅仅记录如何将quartz集成到spring中,不对原理做过多解释,如果有兴趣请期待下篇。
一、集成步骤
1、引入pom依赖
<!--quartz-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
2、配置SchedulerFactoryBean
这一步是关键的一步,SchedulerFactoryBean是spring提供的scheduler工厂类,将quartz实例纳入spring的应用上下文,并由spring管理其生命周期。工厂类提供了配置scheduler实例的属性:
<!-- quartz scheduler配置 -->
<bean name="quartzScheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--配置文件地址-->
<property name="configLocation" value="classpath:quartz.properties" />
<property name="overwriteExistingJobs" value="true" />
<property name="autoStartup" value="false" />
<!--是否允许javamelody监控-->
<property name="exposeSchedulerInRepository" value="false" />
</bean>
3、配置quartz.properties文件
更多更具体的配置,我们一般通过一个properties文件来设置,如下所示:
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName: quartzScheduler
org.quartz.scheduler.instanceId: AUTO
org.quartz.scheduler.skipUpdateCheck: true
#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.misfireThreshold: 10000
#org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
#org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
org.quartz.jobStore.useProperties: true
#org.quartz.jobStore.dataSource: myDS
org.quartz.jobStore.tablePrefix: QRTZ_
org.quartz.jobStore.isClustered: true
#org.quartz.jobStore.clusterCheckinInterval: 7500
org.quartz.jobStore.maxMisfiresToHandleAtATime: 1
我们在工厂类中指定了该配置文件地址,创建scheduler实例的时候,便会加载这些配置文件。
4、在web.xml文件中配置监听器
在web应用中,我们一般通过在web.xml文件中添加监听器,并在其中手动启动scheduler实例。
<listener>
<listener-class>com.gameloft9.demo.jobs.QuartzStartup</listener-class>
</listener>
然后在contextInitialized中启动quartz实例:
package com.gameloft9.demo.jobs;
import com.gameloft9.demo.util.ContextUtil;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* 启动quartz
* Created by gameloft9 on 2019/4/8.
*/
@Slf4j
public class QuartzStartup implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
log.info("Startup quartz.");
try {
Scheduler scheduler = ContextUtil.getBean(Scheduler.class);
scheduler.startDelayed(60);
} catch (SchedulerException e) {
log.error("", e);
}
}
public void contextDestroyed(ServletContextEvent sce) {
log.info("shutdown quartz.");
try {
Scheduler scheduler = ContextUtil.getBean(Scheduler.class);
if(!scheduler.isShutdown()){
scheduler.shutdown();
}
} catch (SchedulerException e) {
log.error("", e);
}
}
}
二、使用示例
我们先新建一个打印日志的定时任务,通过sleep模拟任务执行,并且记录执行次数:
package com.gameloft9.demo.jobs;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
/**
* job示例
* Created by gameloft9 on 2019/4/8.
*/
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
@Slf4j
public class PrintJob implements InterruptableJob {
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
String countStr = context.getJobDetail().getJobDataMap().getString("count");
long count = 0;
if (countStr != null){
count = Long.parseLong(countStr);
}
context.getJobDetail().getJobDataMap().put("count", "" + (count + 1));
// 模拟任务执行
Thread.sleep(1000);
log.info("任务执行成功,累计执行次数:{}",count);
} catch (Exception e) {
log.error("", e);
} finally {
}
}
public void interrupt() throws UnableToInterruptJobException {
// do nothing
}
}
下面是一些增删改,停用,恢复的使用例子:
/**
* 添加任务
* */
@RequestMapping(value = "/addJob.do", method = RequestMethod.POST)
@ResponseBody
public String add(Model model, HttpServletResponse response){
String id = "PrintJob";
String desc = "打印日志";
String cron = "1/10 * * * * ?";// 从1开始每10秒执行一次
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.putAsString("count",0);
JobDetail jobDetail = JobBuilder.newJob(PrintJob.class)
.storeDurably(true).withIdentity(id)
.withDescription(desc).setJobData(jobDataMap).build();
CronScheduleBuilder schBuilder = CronScheduleBuilder.cronSchedule(cron);
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(id).withDescription(desc)
.forJob(jobDetail).withSchedule(schBuilder).build();
Date firstFileTime = null;
try{
quartzScheduler.getScheduler().addJob(jobDetail, false);
firstFileTime = quartzScheduler.getScheduler().scheduleJob(trigger);
}catch(SchedulerException e){
log.info("新增任务失败:{}-{}", id,desc,e);
return "fail";
}
log.info("新增任务成功:{}-{},第一次执行时间为:{}", id,desc,firstFileTime);
return "success";
}
/**
* 更新
* */
@RequestMapping(value = "/updateJob.do", method = RequestMethod.POST)
@ResponseBody
public String update(Model model, HttpServletResponse response){
String id = "PrintJob";
String desc = "打印日志";
String cron = "1/20 * * * * ?";// 从1开始每10秒执行一次
// 先停掉原来的trigger
try{
List<? extends Trigger> triggers = quartzScheduler.getScheduler()
.getTriggersOfJob(new JobKey(id));
for (Trigger t : triggers) {
quartzScheduler.getScheduler().unscheduleJob(t.getKey());
}
}catch(SchedulerException e){
log.error("停掉原有trigger异常",e);
return "fail";
}
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.putAsString("count",0);
JobDetail jobDetail = JobBuilder.newJob(PrintJob.class)
.storeDurably(true).withIdentity(id)
.withDescription(desc).setJobData(jobDataMap).build();
CronScheduleBuilder schBuilder = CronScheduleBuilder.cronSchedule(cron);
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(id).withDescription(desc)
.forJob(jobDetail).withSchedule(schBuilder).build();
Date firstFileTime = null;
try{
quartzScheduler.getScheduler().addJob(jobDetail, true);// 替换
firstFileTime = quartzScheduler.getScheduler().scheduleJob(trigger);
}catch(SchedulerException e){
log.info("更新任务失败:{}-{}", id,desc,e);
return "fail";
}
log.info("更新任务成功:{}-{},第一次执行时间为:{}", id,desc,firstFileTime);
return "success";
}
/**
* 立即执行任务
* */
@RequestMapping(value = "/runJobNow.do", method = RequestMethod.POST)
@ResponseBody
public String run(Model model){
String id = "PrintJob";
Trigger trigger = TriggerBuilder.newTrigger().forJob(id)
.startNow().build();
try{
Date startDate = quartzScheduler.getScheduler().scheduleJob(trigger);
log.info("任务启动成功:{},执行时间为:{}", id,startDate);
return "success";
}catch(SchedulerException e){
log.error("启动失败",e);
return "fail";
}
}
/**
* 停止任务计划
* */
@RequestMapping(value = "/pauseJob.do", method = RequestMethod.POST)
@ResponseBody
public String pause(Model model){
String id = "PrintJob";
try{
quartzScheduler.getScheduler().pauseJob(new JobKey(id));
log.info("停用任务成功:{}", id);
return "success";
}catch(SchedulerException e){
log.error("停用任务失败",e);
return "fail";
}
}
/**
* 恢复任务计划
* */
@RequestMapping(value = "/resumeJob.do", method = RequestMethod.POST)
@ResponseBody
public String resume(Model model){
String id = "PrintJob";
try{
quartzScheduler.getScheduler().resumeJob(new JobKey(id));
log.info("回复任务计划成功:{}", id);
return "success";
}catch(SchedulerException e){
log.error("回复任务计划失败",e);
return "fail";
}
}
/**
* 删除任务
* */
@RequestMapping(value = "/deleteJob.do", method = RequestMethod.POST)
@ResponseBody
public String delete(Model model){
String id = "PrintJob";
JobKey jobKey = new JobKey(id);
try{
List<? extends Trigger> triggers = quartzScheduler.getScheduler()
.getTriggersOfJob(jobKey);
for (Trigger t : triggers) {
quartzScheduler.getScheduler().unscheduleJob(t.getKey());
}
quartzScheduler.getScheduler().deleteJob(jobKey);
log.info("删除任务成功:{}",jobKey.getName());
return "success";
}catch(SchedulerException e){
log.error("删除任务异常",e);
return "fail";
}
}