思考:
Quartz的核心组成:
Quartz任务调度基本实现原理:
Quartz 任务调度的核心元素是 scheduler, trigger 和 job,其中 trigger 和 job 是任务调度的元数据,scheduler 是实际执行调度的控制器。
Trigger 是用于定义调度时间的元素,即按照什么时间规则去执行任务。
Quartz 中主要提供了四种类型的 trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger,和 NthIncludedDayTrigger。
Job 用于表示被调度的任务。
主要有两种类型的 job:无状态的(stateless)和有状态的(stateful)。
对于同一个 trigger 来说,有状态的 job 不能被并行执行,只有上一次触发的任务被执行完之后,才能触发下一次执行。
Job 主要有两种属性:volatility 和 durability.
其中 volatility 表示任务是否被持久化到数据库存储,而 durability 表示在没有 trigger 关联的时候任务是否被保留。两者都是在值为 true 的时候任务被持久化或保留。一个 job 可以被多个 trigger 关联,但是一个 trigger 只能关联一个 job。
scheduler 由 scheduler 工厂创建:
DirectSchedulerFactory 或者 StdSchedulerFactory(使用较多)。
Scheduler 主要有三种:RemoteMBeanScheduler, RemoteScheduler 和 StdScheduler。
<dependencies>
<!--quartz依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<!--阿里druid 数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.11</version>
</dependency>
<!--mybatis包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--mysql包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--springboot选择web自带依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--分页工具-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.0.0</version>
</dependency>
<!--数据源-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
主要引入的依赖包:quartz,MySQL,mybatis,数据源依赖,分页工具。
主要针对数据库和mybatis进行的配置
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:mappers/*Mapper.xml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/qrtz?useUnicode=true&characterEncoding=utf8
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
在application.yml 同级目录下创建一个quartz.properties的文件,这个文件主要是对 Quartz 进行的配置。
# 固定前缀org.quartz
# 主要分为scheduler、threadPool、jobStore、plugin等部分
#
#
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
#如果您希望Quartz Scheduler通过RMI作为服务器导出本身,则将“rmi.export”标志设置为true。
#在同一个配置文件中为'org.quartz.scheduler.rmi.export'和'org.quartz.scheduler.rmi.proxy'指定一个'true'值是没有意义的,如果你这样做,'export '选项将被忽略
org.quartz.scheduler.rmi.export = false
#如果要连接(使用)远程服务的调度程序,则将“org.quartz.scheduler.rmi.proxy”标志设置为true。您还必须指定RMI注册表进程的主机和端口 - 通常是“localhost”端口1099。
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
# 实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
# threadCount和threadPriority将以setter的形式注入ThreadPool实例
# 并发个数 如果你只有几个工作每天触发几次 那么1个线程就可以,如果你有成千上万的工作,每分钟都有很多工作 那么久需要50-100之间.
# 只有1到100之间的数字是非常实用的
org.quartz.threadPool.threadCount = 5
# 优先级 默认值为5
org.quartz.threadPool.threadPriority = 5
#可以是“true”或“false”,默认为false。
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
#在被认为“misfired”(失火)之前,调度程序将“tolerate(容忍)”一个Triggers(触发器)将其下一个启动时间通过的毫秒数。默认值(如果您在配置中未输入此属性)为60000(60秒)
org.quartz.jobStore.misfireThreshold = 5000
# 默认存储在内存中,RAMJobStore快速轻便,但是当进程终止时,所有调度信息都会丢失。
#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
#持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#您需要为JobStore选择一个DriverDelegate才能使用。DriverDelegate负责执行特定数据库可能需要的任何JDBC工作。
# StdJDBCDelegate是一个使用“vanilla”JDBC代码(和SQL语句)来执行其工作的委托,用于完全符合JDBC的驱动程序
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#可以将“org.quartz.jobStore.useProperties”配置参数设置为“true”(默认为false),以指示JDBCJobStore将JobDataMaps中的所有值都作为字符串,
# 因此可以作为名称 - 值对存储而不是在BLOB列中以其序列化形式存储更多复杂的对象。从长远来看,这是更安全的,因为您避免了将非String类序列化为BLOB的类版本问题。
org.quartz.jobStore.useProperties=true
#表前缀
org.quartz.jobStore.tablePrefix = QRTZ_
#您需要设置JobStore应该使用哪个DataSource。
org.quartz.jobStore.dataSource = qzDS
org.quartz.dataSource.qzDS.driver = com.mysql.cj.jdbc.Driver
org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/qrtz?useUnicode=true&characterEncoding=utf8
org.quartz.dataSource.qzDS.user = root
org.quartz.dataSource.qzDS.password = root
org.quartz.dataSource.qzDS.maxConnections = 10
#设置为“true”以打开群集功能。如果您有多个Quartz实例使用同一组数据库表,则此属性必须设置为“true”,否则您将遇到破坏。
#org.quartz.jobStore.isClustered=false
接着我们需要写一个 SchedulerConfig.java 文件,读取我们刚刚配置的quartz.properties文件中的属性。用于注入Scheduler相关的Bean。
@Configuration
public class SchedulerConfig {
@Bean(name="SchedulerFactory")
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setQuartzProperties(quartzProperties());
return factory;
}
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
//在quartz.properties中的属性被读取并注入后再初始化对象
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
/**
* quartz初始化监听器
* 这个监听器可以监听到工程的启动,在工程停止再启动时可以让已有的定时任务继续进行。
* @return
*/
@Bean
public QuartzInitializerListener executorListener() {
return new QuartzInitializerListener();
}
/**
*
*通过SchedulerFactoryBean获取Scheduler的实例
*/
@Bean(name="Scheduler")
public Scheduler scheduler() throws IOException {
return schedulerFactoryBean().getScheduler();
}
}
这时我们已经将Quartz与Springboot简单的整合到一起,可以在下面的controller中通过注解注入scheduler,拿到scheduler实例而不是用工厂类去创建。
如何实现quartz的数据持久化?
默认情况下,Quartz是将我们的定时任务的记录保存到内存里,等我们再次启动项目的时候,我们之前设置的定时任务都会被清空,无法持久化。
当然Quartz可以将记录持久化到数据库中,可以从自定义DataSource持久化数据和使用Springboot的DataSource两方面来持久化Quartz的数据
(如何使用springboot中的数据源?
参考:https://blog.csdn.net/shirukai/article/details/82590099)
- 自定义 DataSource
上述 quartz.properties 中设置了数据源配置
这样配置之后我们就可以将Quartz数据持久化到我们指定的数据库了,此时,我们还需要在数据库中创建所需要的表(一般使用quartz默认表,sql文件可以直接下载 官网地址:http://www.quartz-scheduler.org/downloads/)
下面是SQL语句,可以直接复制创建表格。
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for `QRTZ_BLOB_TRIGGERS`
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_BLOB_TRIGGERS`;
CREATE TABLE `QRTZ_BLOB_TRIGGERS` (
`SCHED_NAME` varchar(120) NOT NULL,
`TRIGGER_NAME` varchar(200) NOT NULL,
`TRIGGER_GROUP` varchar(200) NOT NULL,
`BLOB_DATA` blob,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
CONSTRAINT `QRTZ_BLOB_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Table structure for `QRTZ_CALENDARS`
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_CALENDARS`;
CREATE TABLE `QRTZ_CALENDARS` (
`SCHED_NAME` varchar(120) NOT NULL,
`CALENDAR_NAME` varchar(200) NOT NULL,
`CALENDAR` blob NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`CALENDAR_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Table structure for `QRTZ_CRON_TRIGGERS`
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_CRON_TRIGGERS`;
CREATE TABLE `QRTZ_CRON_TRIGGERS` (
`SCHED_NAME` varchar(120) NOT NULL,
`TRIGGER_NAME` varchar(200) NOT NULL,
`TRIGGER_GROUP` varchar(200) NOT NULL,
`CRON_EXPRESSION` varchar(200) NOT NULL,
`TIME_ZONE_ID` varchar(80) DEFAULT NULL,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
CONSTRAINT `QRTZ_CRON_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Table structure for `QRTZ_FIRED_TRIGGERS`
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_FIRED_TRIGGERS`;
CREATE TABLE `QRTZ_FIRED_TRIGGERS` (
`SCHED_NAME` varchar(120) NOT NULL,
`ENTRY_ID` varchar(95) NOT NULL,
`TRIGGER_NAME` varchar(200) NOT NULL,
`TRIGGER_GROUP` varchar(200) NOT NULL,
`INSTANCE_NAME` varchar(200) NOT NULL,
`FIRED_TIME` bigint(13) NOT NULL,
`SCHED_TIME` bigint(13) NOT NULL,
`PRIORITY` int(11) NOT NULL,
`STATE` varchar(16) NOT NULL,
`JOB_NAME` varchar(200) DEFAULT NULL,
`JOB_GROUP` varchar(200) DEFAULT NULL,
`IS_NONCONCURRENT` varchar(1) DEFAULT NULL,
`REQUESTS_RECOVERY` varchar(1) DEFAULT NULL,
PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Table structure for `QRTZ_JOB_DETAILS`
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_JOB_DETAILS`;
CREATE TABLE `QRTZ_JOB_DETAILS` (
`SCHED_NAME` varchar(120) NOT NULL,
`JOB_NAME` varchar(200) NOT NULL,
`JOB_GROUP` varchar(200) NOT NULL,
`DESCRIPTION` varchar(250) DEFAULT NULL,
`JOB_CLASS_NAME` varchar(250) NOT NULL,
`IS_DURABLE` varchar(1) NOT NULL,
`IS_NONCONCURRENT` varchar(1) NOT NULL,
`IS_UPDATE_DATA` varchar(1) NOT NULL,
`REQUESTS_RECOVERY` varchar(1) NOT NULL,
`JOB_DATA` blob,
PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Table structure for `QRTZ_LOCKS`
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_LOCKS`;
CREATE TABLE `QRTZ_LOCKS` (
`SCHED_NAME` varchar(120) NOT NULL,
`LOCK_NAME` varchar(40) NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Table structure for `QRTZ_PAUSED_TRIGGER_GRPS`
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_PAUSED_TRIGGER_GRPS`;
CREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS` (
`SCHED_NAME` varchar(120) NOT NULL,
`TRIGGER_GROUP` varchar(200) NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Table structure for `QRTZ_SCHEDULER_STATE`
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_SCHEDULER_STATE`;
CREATE TABLE `QRTZ_SCHEDULER_STATE` (
`SCHED_NAME` varchar(120) NOT NULL,
`INSTANCE_NAME` varchar(200) NOT NULL,
`LAST_CHECKIN_TIME` bigint(13) NOT NULL,
`CHECKIN_INTERVAL` bigint(13) NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Table structure for `QRTZ_SIMPLE_TRIGGERS`
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_SIMPLE_TRIGGERS`;
CREATE TABLE `QRTZ_SIMPLE_TRIGGERS` (
`SCHED_NAME` varchar(120) NOT NULL,
`TRIGGER_NAME` varchar(200) NOT NULL,
`TRIGGER_GROUP` varchar(200) NOT NULL,
`REPEAT_COUNT` bigint(7) NOT NULL,
`REPEAT_INTERVAL` bigint(12) NOT NULL,
`TIMES_TRIGGERED` bigint(10) NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
CONSTRAINT `QRTZ_SIMPLE_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Table structure for `QRTZ_SIMPROP_TRIGGERS`
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_SIMPROP_TRIGGERS`;
CREATE TABLE `QRTZ_SIMPROP_TRIGGERS` (
`SCHED_NAME` varchar(120) NOT NULL,
`TRIGGER_NAME` varchar(200) NOT NULL,
`TRIGGER_GROUP` varchar(200) NOT NULL,
`STR_PROP_1` varchar(512) DEFAULT NULL,
`STR_PROP_2` varchar(512) DEFAULT NULL,
`STR_PROP_3` varchar(512) DEFAULT NULL,
`INT_PROP_1` int(11) DEFAULT NULL,
`INT_PROP_2` int(11) DEFAULT NULL,
`LONG_PROP_1` bigint(20) DEFAULT NULL,
`LONG_PROP_2` bigint(20) DEFAULT NULL,
`DEC_PROP_1` decimal(13,4) DEFAULT NULL,
`DEC_PROP_2` decimal(13,4) DEFAULT NULL,
`BOOL_PROP_1` varchar(1) DEFAULT NULL,
`BOOL_PROP_2` varchar(1) DEFAULT NULL,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
CONSTRAINT `QRTZ_SIMPROP_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Table structure for `QRTZ_TRIGGERS`
-- ----------------------------
DROP TABLE IF EXISTS `QRTZ_TRIGGERS`;
CREATE TABLE `QRTZ_TRIGGERS` (
`SCHED_NAME` varchar(120) NOT NULL,
`TRIGGER_NAME` varchar(200) NOT NULL,
`TRIGGER_GROUP` varchar(200) NOT NULL,
`JOB_NAME` varchar(200) NOT NULL,
`JOB_GROUP` varchar(200) NOT NULL,
`DESCRIPTION` varchar(250) DEFAULT NULL,
`NEXT_FIRE_TIME` bigint(13) DEFAULT NULL,
`PREV_FIRE_TIME` bigint(13) DEFAULT NULL,
`PRIORITY` int(11) DEFAULT NULL,
`TRIGGER_STATE` varchar(16) NOT NULL,
`TRIGGER_TYPE` varchar(8) NOT NULL,
`START_TIME` bigint(13) NOT NULL,
`END_TIME` bigint(13) DEFAULT NULL,
`CALENDAR_NAME` varchar(200) DEFAULT NULL,
`MISFIRE_INSTR` smallint(2) DEFAULT NULL,
`JOB_DATA` blob,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
KEY `SCHED_NAME` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),
CONSTRAINT `QRTZ_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`) REFERENCES `QRTZ_JOB_DETAILS` (`SCHED_NAME`, `JOB_NAME`, `JOB_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
SET FOREIGN_KEY_CHECKS = 1;
Job类:每个类就是需要被执行的定时任务。
首先设置一个BaseJob接口,用来继承 Job类 :
然后设置两个类实现BaseJob接口,去具体实现每个定时任务具体需要执行的工作。
public interface BaseJob extends Job {
public void execute(JobExecutionContext context) throws JobExecutionException;
}
eg:我们这里写了两个具体类 NewJob 和 HelloJob.两个类需要继承org.quartz.Job接口并实现接口里的execute方法,
public class HelloJob implements BaseJob {
private static Logger log = LoggerFactory.getLogger(HelloJob.class);
public HelloJob() {
}
public void execute(JobExecutionContext context) throws JobExecutionException {
log.info("Hello Job执行时间: " + new Date());
}
}
NewJob 同上,可以自己修改内部实现什么具体功能。
实体类:
JobAndTrigger.java:
package com.xumin.quartz2.model;
import java.math.BigInteger;
public class JobAndTrigger {
private String JOB_NAME;
private String JOB_GROUP;
private String JOB_CLASS_NAME;
private String TRIGGER_NAME;
private String TRIGGER_GROUP;
private BigInteger REPEAT_INTERVAL;
private BigInteger TIMES_TRIGGERED;
private String CRON_EXPRESSION;
private String TIME_ZONE_ID;
public String getJOB_NAME() {
return JOB_NAME;
}
public void setJOB_NAME(String jOB_NAME) {
JOB_NAME = jOB_NAME;
}
public String getJOB_GROUP() {
return JOB_GROUP;
}
public void setJOB_GROUP(String jOB_GROUP) {
JOB_GROUP = jOB_GROUP;
}
public String getJOB_CLASS_NAME() {
return JOB_CLASS_NAME;
}
public void setJOB_CLASS_NAME(String jOB_CLASS_NAME) {
JOB_CLASS_NAME = jOB_CLASS_NAME;
}
public String getTRIGGER_NAME() {
return TRIGGER_NAME;
}
public void setTRIGGER_NAME(String tRIGGER_NAME) {
TRIGGER_NAME = tRIGGER_NAME;
}
public String getTRIGGER_GROUP() {
return TRIGGER_GROUP;
}
public void setTRIGGER_GROUP(String tRIGGER_GROUP) {
TRIGGER_GROUP = tRIGGER_GROUP;
}
public BigInteger getREPEAT_INTERVAL() {
return REPEAT_INTERVAL;
}
public void setREPEAT_INTERVAL(BigInteger rEPEAT_INTERVAL) {
REPEAT_INTERVAL = rEPEAT_INTERVAL;
}
public BigInteger getTIMES_TRIGGERED() {
return TIMES_TRIGGERED;
}
public void setTIMES_TRIGGERED(BigInteger tIMES_TRIGGERED) {
TIMES_TRIGGERED = tIMES_TRIGGERED;
}
public String getCRON_EXPRESSION() {
return CRON_EXPRESSION;
}
public void setCRON_EXPRESSION(String cRON_EXPRESSION) {
CRON_EXPRESSION = cRON_EXPRESSION;
}
public String getTIME_ZONE_ID() {
return TIME_ZONE_ID;
}
public void setTIME_ZONE_ID(String tIME_ZONE_ID) {
TIME_ZONE_ID = tIME_ZONE_ID;
}
}
JobInfo.java:
为了方便增加定时任务的操作,封装了传入参数,作为一个对象插入。
package com.xumin.quartz2.model;
public class JobInfo {
private String jobClassName;
private String jobGroupName;
private String cronExpression;
private String jobType;
private Integer timeType;
public String getJobClassName() {
return "com.xumin.quartz2.jobs."+jobClassName.trim();
}
public void setJobClassName(String jobClassName) {
this.jobClassName = jobClassName;
}
public String getJobGroupName() {
return jobGroupName;
}
public void setJobGroupName(String jobGroupName) {
this.jobGroupName = jobGroupName;
}
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
public String getJobType() {
return jobType;
}
public void setJobType(String jobType) {
this.jobType = jobType;
}
public Integer getTimeType() {
return timeType;
}
public void setTimeType(Integer timeType) {
this.timeType = timeType;
}
}
Controller 类:
与前端对接,通过不同的方法来控制管理定时任务的添加,暂停,修改,删除,查询。
package com.xumin.quartz2.controller;
import com.github.pagehelper.PageInfo;
import com.xumin.quartz2.model.JobAndTrigger;
import com.xumin.quartz2.model.JobInfo;
import com.xumin.quartz2.service.BaseJob;
import com.xumin.quartz2.service.IJobAndTriggerService;
import com.xumin.quartz2.tool.DateUnit;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
import static org.quartz.DateBuilder.futureDate;
@RestController
@RequestMapping(value = "job")
public class JobController {
@Autowired
private IJobAndTriggerService iJobAndTriggerService;
//加入Qulifier注解,通过名称注入bean
@Autowired
@Qualifier("Scheduler")
private Scheduler scheduler;
@Autowired
private DateUnit dateUnit;
private static Logger log = LoggerFactory.getLogger(JobController.class);
/**
* 添加任务
*
* @param jobInfo
* @throws Exception
*/
@PostMapping(value = "/addjob")
// public void addjob(@RequestBody JobInfo jobInfo) throws Exception {
public void addjob(@RequestParam(value = "jobClassName") String jobClassName,
@RequestParam(value = "jobGroupName") String jobGroupName,
@RequestParam(value = "cronExpression") String cronExpression) throws Exception {
JobInfo jobInfo = new JobInfo();
jobInfo.setJobClassName(jobClassName);
jobInfo.setJobGroupName(jobGroupName);
jobInfo.setCronExpression(cronExpression);
if ("".equals(jobInfo.getJobClassName()) || "".equals(jobInfo.getJobGroupName()) || "".equals(jobInfo.getCronExpression())) {
return;
}
if (jobInfo.getTimeType() == null) {
addCronJob(jobInfo);
return;
}
addSimpleJob(jobInfo);
}
//CronTrigger
public void addCronJob(JobInfo jobInfo) throws Exception {
// 启动调度器
scheduler.start();
//构建job信息
JobDetail jobDetail = JobBuilder.newJob(getClass(jobInfo.getJobClassName()).getClass()).
withIdentity(jobInfo.getJobClassName(), jobInfo.getJobGroupName())
.build();
//表达式调度构建器(即任务执行的时间)
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobInfo.getCronExpression());
//按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().
withIdentity(jobInfo.getJobClassName(), jobInfo.getJobGroupName())
.withSchedule(scheduleBuilder)
.build();
try {
scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
System.out.println("创建定时任务失败" + e);
throw new Exception("创建定时任务失败");
}
}
//Simple Trigger
public void addSimpleJob(JobInfo jobInfo) throws Exception {
// 启动调度器
scheduler.start();
//构建job信息
JobDetail jobDetail = JobBuilder.newJob(getClass(jobInfo.getJobClassName()).getClass())
.withIdentity(jobInfo.getJobClassName(), jobInfo.getJobGroupName())
.build();
DateBuilder.IntervalUnit verDate = dateUnit.verification(jobInfo.getTimeType());
SimpleTrigger simpleTrigger = (SimpleTrigger) TriggerBuilder.newTrigger()
.withIdentity(jobInfo.getJobClassName(), jobInfo.getJobGroupName())
.startAt(futureDate(Integer.parseInt(jobInfo.getCronExpression()), verDate))
.forJob(jobInfo.getJobClassName(), jobInfo.getJobGroupName())
.build();
try {
scheduler.scheduleJob(jobDetail, simpleTrigger);
} catch (SchedulerException e) {
System.out.println("创建定时任务失败" + e);
throw new Exception("创建定时任务失败");
}
}
/**
* 暂停任务
*
* @param jobClassName
* @param jobGroupName
* @throws Exception
*/
@PostMapping(value = "/pausejob")
public void pausejob(@RequestParam(value = "jobClassName") String jobClassName, @RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
jobPause(jobClassName, jobGroupName);
}
public void jobPause(String jobClassName, String jobGroupName) throws Exception {
scheduler.pauseJob(JobKey.jobKey(jobClassName, jobGroupName));
}
/**
* 恢复任务
*
* @param jobClassName
* @param jobGroupName
* @throws Exception
*/
@PostMapping(value = "/resumejob")
public void resumejob(@RequestParam(value = "jobClassName") String jobClassName, @RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
jobresume(jobClassName, jobGroupName);
}
public void jobresume(String jobClassName, String jobGroupName) throws Exception {
scheduler.resumeJob(JobKey.jobKey(jobClassName, jobGroupName));
}
/**
* 更新任务
*
* @param jobClassName
* @param jobGroupName
* @param cronExpression
* @throws Exception
*/
@PostMapping(value = "/reschedulejob")
public void rescheduleJob(@RequestParam(value = "jobClassName") String jobClassName,
@RequestParam(value = "jobGroupName") String jobGroupName,
@RequestParam(value = "cronExpression") String cronExpression) throws Exception {
jobreschedule(jobClassName, jobGroupName, cronExpression);
}
public void jobreschedule(String jobClassName, String jobGroupName, String cronExpression) throws Exception {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobClassName, jobGroupName);
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
// 按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
System.out.println("更新定时任务失败" + e);
throw new Exception("更新定时任务失败");
}
}
/**
* 删除任务
* 删除操作前应该暂停该任务的触发器,并且停止该任务的执行
*
* @param jobClassName
* @param jobGroupName
* @throws Exception
*/
@PostMapping(value = "/deletejob")
public void deletejob(@RequestParam(value = "jobClassName") String jobClassName, @RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
jobdelete(jobClassName, jobGroupName);
}
public void jobdelete(String jobClassName, String jobGroupName) throws Exception {
scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName, jobGroupName));
scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName, jobGroupName));
scheduler.deleteJob(JobKey.jobKey(jobClassName, jobGroupName));
}
/**
* 查询任务
*
* @param pageNum
* @param pageSize
* @return
*/
@GetMapping(value = "/queryjob")
public Map<String, Object> queryjob(@RequestParam(value = "pageNum") Integer pageNum, @RequestParam(value = "pageSize") Integer pageSize) {
PageInfo<JobAndTrigger> jobAndTrigger = iJobAndTriggerService.getJobAndTriggerDetails(pageNum, pageSize);
Map<String, Object> map = new HashMap<String, Object>();
map.put("JobAndTrigger", jobAndTrigger);
map.put("number", jobAndTrigger.getTotal());
return map;
}
/**
* 根据类名称,通过反射得到该类,然后创建一个BaseJob的实例。
* 由于NewJob和HelloJob都实现了BaseJob,
* 所以这里不需要我们手动去判断。这里涉及到了一些java多态调用的机制
*
* @param classname
* @return
* @throws Exception
*/
public static BaseJob getClass(String classname) throws Exception {
Class<?> class1 = Class.forName(classname);
return (BaseJob) class1.newInstance();
}
}
ps:可能出现报错:
此时如果只配置该文件会出现DefaultScheduler已经在scheduler properties 中注册相关报错.
此时,我们可以初步定位到是quartz.properties 配置文件中的声明DefaultScheduler出现了问题。之前考虑了是定义默认scheduler的名字重复,修改了默认名字后任然报错,后来思考可能是重复读取了这句配置语句,注释掉后,报错问题解决。
(之前参考了很多博主的代码发现,他们配置这一行,并没有出现错误,具体原因不知道是什么,猜想为版本问题,此处项目使用5.1.3 版,5.0.9版本可以直接运行!不知道是不是这个坑~)
本文是参考了 :
天降风云博主(https://blog.csdn.net/u012907049/article/details/73801122/)
haoxiaoyong1014博主(https://blog.csdn.net/haoxiaoyong1014/article/details/83339169)
shirukai博主(https://blog.csdn.net/shirukai/article/details/82590099)