简介
看完上篇之后,你会发现spring自带的schedule确实方便,而且十分简洁。但是我们在平常开发中时常会有面临着需求的改变,比如这个方法本来是中午12点执行的,现在要变成下午1点去执行,然而项目已经上线了,修改源码中@Scheduled注解中的cron表达式显然是不科学的。那么面对这种情况quartz给我们提供了很好的管理功能。
springboot整合quartz
github::https://github.com/mzd123/springboot_quartz
版本: boot:1.5.9.RELEASE
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>HikariCP-java6</artifactId>
<groupId>com.zaxxer</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.7-dmr</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<!-- 通用mapper -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>1.1.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
创建表结构: 刚开始学这个的时候,我发现所有关于quartz动态管理的项目表都是一样,最开始我以为都是抄的。每一个有自己想法的,后来我发现,这个表结构是quartz自带的。sql脚本位于:org.quartz.impl.jdbcjobstore包中。
quartz表: 这里我用的是:mysql_innodb.sql
配置quartz.properties:
#这个东西随便取的,自己开心就好
org.quartz.scheduler.instanceName=MyScheduler
#最多有5个任务同时在执行,如果有第六个任务将会被搁置
org.quartz.threadPool.threadCount=5
#如果用内存记录定时任务信息,应用重新启动后,定时任务信息将会丢失。
# 比如,用户A通过系统设置1小时后执行Z操作,设置好后的,因系统重新启动,新启动的系统将会丢失“1小时后执行Z操作”的定时任务。
#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
#如果,我们需要在系统意外(或非意外)重新启动后,仍保留定时任务信息,可以使用数据库存储定时任务信息。
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
#方言:org.quartz.impl.jdbcjobstore.StdJDBCDelegate---用于完全符合JDBC的驱动程序
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#表的前缀--- 默认就是QRTZ_
org.quartz.jobStore.tablePrefix=QRTZ_
#配置quartz数据库,优势就是可以和业务数据库分开,相当于配置了两个数据库
org.quartz.jobStore.dataSource=myDS
org.quartz.dataSource.myDS.driver=com.mysql.cj.jdbc.Driver
org.quartz.dataSource.myDS.URL=jdbc:mysql://localhost:3307/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
org.quartz.dataSource.myDS.user=root
org.quartz.dataSource.myDS.password=root
#最大连接数
org.quartz.dataSource.myDS.maxConnections=5
配置quartz.config:
@Configuration
public class SchedulerConfig {
@Bean(name = "SchedulerFactory")
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setAutoStartup(true);
factory.setStartupDelay(5);//延时5秒启动
factory.setQuartzProperties(quartzProperties());
return factory;
}
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
//这个东西如果不配置的话,quartz有自己默认的配置文件
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
/*
* 通过SchedulerFactoryBean获取Scheduler
*/
@Bean(name = "Scheduler")
public Scheduler scheduler() throws IOException {
return schedulerFactoryBean().getScheduler();
}
}
编写quartz的接口:
@RestController
public class JobController {
private final static Logger LOGGER = LoggerFactory.getLogger(JobController.class);
private final String baseuri = "/job";
@Autowired
@Qualifier("Scheduler")
private Scheduler scheduler;
@Autowired
private JobService jobService;
/**
* 新增任务
*
* @param quartz
* @return
*/
@RequestMapping(value = baseuri + "/addschedule")
public Result save(QuartzBean quartz) throws Exception {
LOGGER.info("新增任务");
int i = 100 / 0;
if (quartz.getOldJobGroup() != null) {
JobKey key = new JobKey(quartz.getOldJobName(), quartz.getOldJobGroup());
scheduler.deleteJob(key);
}
//获取.class
Class cls = Class.forName(quartz.getJobClassName());
cls.newInstance();
//创建jobdetail
JobDetail job = JobBuilder.newJob(cls).withIdentity(quartz.getJobName(),
quartz.getJobGroup())
//设置参数
//.usingJobData("aa", "ceshi")
//描述
.withDescription(quartz.getDescription())
.build();
// 使用cron表达式
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(quartz.getCronExpression());
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger" + quartz.getJobName(), quartz.getJobGroup())
.startNow()
.withSchedule(cronScheduleBuilder)
.build();
//交由Scheduler安排触发
scheduler.scheduleJob(job, trigger);
return new Result("200", "", null);
}
/**
* 获取任务列表
*
* @param name
* @return
*/
@RequestMapping(value = baseuri + "/getlist4schedule")
public Result list(String name) {
LOGGER.info("任务列表");
List<QuartzBean> list = jobService.listQuartzBean(name);
return new Result("200", "", list);
}
/**
* 立即执行
*
* @param quartz
* @return
*/
@RequestMapping(value = baseuri + "/doschedule")
public Result trigger(QuartzBean quartz) throws Exception {
LOGGER.info("立即执行");
JobKey key = new JobKey(quartz.getJobName(), quartz.getJobGroup());
scheduler.triggerJob(key);
return new Result("200", "", null);
}
/**
* 暂停任务
*
* @param quartz
* @return
*/
@RequestMapping(value = baseuri + "/pauseschedule")
public Result pause(QuartzBean quartz) throws Exception {
LOGGER.info("停止任务");
JobKey key = new JobKey(quartz.getJobName(), quartz.getJobGroup());
scheduler.pauseJob(key);
return new Result("200", "", null);
}
/**
* 从暂停中恢复过来
*
* @param quartz
* @return
*/
@RequestMapping(value = baseuri + "/recoverschedule")
public Result resume(QuartzBean quartz) throws Exception {
LOGGER.info("恢复任务");
JobKey key = new JobKey(quartz.getJobName(), quartz.getJobGroup());
scheduler.resumeJob(key);
return new Result("200", "", null);
}
/**
* 删除任务
*
* @param quartz
* @return
*/
@RequestMapping(value = baseuri + "/deleteschedule")
public Result remove(QuartzBean quartz) throws Exception {
LOGGER.info("删除任务");
TriggerKey triggerKey = TriggerKey.triggerKey(quartz.getJobName(), quartz.getJobGroup());
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务
scheduler.deleteJob(JobKey.jobKey(quartz.getJobName(), quartz.getJobGroup()));
System.out.println("removeJob:" + JobKey.jobKey(quartz.getJobName()));
return new Result("200", "", null);
}
}
编写接口错误拦截返回:
@ControllerAdvice
public class ErrorController {
private final static Logger logger = LoggerFactory.getLogger(ErrorController.class);
@ExceptionHandler(Exception.class)
@ResponseBody
public Result error(Throwable throwable) {
System.out.println("aaa");
logger.error(throwable.toString());
return new Result("500", throwable.toString(), null);
}
}
疑问:
以上代码就能实现springboot对quartz的管理,增删改查、立即执行、暂停、恢复等操作,但是任有不少疑问。。。
1、为什么我们并没有对数据库进行操作,数据库中就有了quartz的数据?
2、 我们配置了threadCount为5,那么如果有5个任务还在执行的时候触发了第六个任务会怎么样?
3、如果任务刚执行完,把系统时间修改为任务触发时间之前,那相同时间还会再触发一次吗?