前言
Quartz 是一个定时任务调度框架,简单易用,功能强大可以使实现定时任务的。在项目开发过程当中,某些定时任务,可能在运行一段时间之后,就不需要了,或者需要修改下定时任务的执行时间等等。需要在代码当中进行修改然后重新打包发布,很麻烦。使用Quartz来实现的话不需要重新修改代码而达到要求。
Quartz 核心概念
- Job:我们要定时执行某件事情,然后我们把它写成一个 java 类,这就是 Job
- JobDetail:具体任务类有了,还需要任务详情类( JobDetail )去构建 Job。
- Trigger 触发器:你把任务放进去了,什么时候执行?这就是触发器的作用。它根据你 cron 表达式的时间去触发任务详情。一个触发器对应一个任务详情。
- Scheduler调度器:一个调度容器中可以注册多个 JobDetail 和 Trigger。当 Trigger 与 JobDetail 组合,就可以被 Scheduler 容器调度了。
Quartz 作业存储类型
- RAMJobStore:RAM 也就是内存,默认情况下 Quartz 会将任务调度存储在内存中,内存的速度是最快的。不好的地方就是数据缺乏持久性,但程序崩溃或者重新发布的时候,所有运行信息都会丢失。
- JDBC 作业存储:存到数据库之后,可以做单点也可以做集群,当任务多了之后,可以统一进行管理,随时停止、暂停、修改任务。关闭或者重启服务器,运行的信息都不会丢失。缺点就是运行速度快慢取决于连接数据库的快慢。
Quartz 工作原理
Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。
Quartz 的集群模式指的是一个集群下多个节点管理同一批任务的调度,通过共享数据库的方式实现,保证同一个任务到达触发时间的时候,只有一台机器去执行该任务。每个节点部署一个单独的 quartz 实例,相互之间没有直接数据通信,quartz 集群是通过数据库表来感知其他的应用的,各个节点之间并没有直接的通信。只有使用持久的 JobStore 才能完成 Quartz 集群。
/**
* @Description: 1.Scheduler 调度程序 scheduler是一个计划调度器容器(总部),容器里面可以盛放众多的JobDetail和trigger,
* 当容器启动后,里面的每个JobDetail都会根据trigger按部就班自动去执行,scheduler是个容器,
* 容器中有一个线程池,用来并行调度执行每个作业,这样可以提高容器效率
*
* 2.Trigger 触发器 Trigger代表一个调度参数的配置,什么时候去调,当JobDetail和Trigger在scheduler容器上注册后,
* 形成了装配好的作业(JobDetail和Trigger所组成的一对儿),就可以伴随容器启动而调度执行了
*
* 3.Job&JobDetail 工作 Quartz将任务分为Job、JobDetail两部分,其中Job用来定义任务的执行逻辑,
* 而JobDetail用来描述Job的定义(例如Job接口的实现类以及其他相关的静态信息)。
*
* StdSchedulerFactory
* ↓
* Scheduler
* ↓ ↓
* Job ← JobDetail ··· Trigger
* 1:n
* @Author: yangjj_t
* @Date: 2021/10/18 15:01
*/
Spring Boot 整合 Quartz 定时调度框架
- 引入 quartz 依赖
<!--quartz依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
- 数据库建 quartz 相关表
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;
CREATE TABLE `QRTZ_CALENDARS` (
`SCHED_NAME` VARCHAR(120) NOT NULL,
`CALENDAR_NAME` VARCHAR(190) NOT NULL,
`CALENDAR` BLOB NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`CALENDAR_NAME`)
);
CREATE TABLE `QRTZ_FIRED_TRIGGERS` (
`SCHED_NAME` VARCHAR(120) NOT NULL,
`ENTRY_ID` VARCHAR(95) NOT NULL,
`TRIGGER_NAME` VARCHAR(190) NOT NULL,
`TRIGGER_GROUP` VARCHAR(190) NOT NULL,
`INSTANCE_NAME` VARCHAR(190) NOT NULL,
`FIRED_TIME` BIGINT NOT NULL,
`SCHED_TIME` BIGINT NOT NULL,
`PRIORITY` INT NOT NULL,
`STATE` VARCHAR(16) NOT NULL,
`JOB_NAME` VARCHAR(190) DEFAULT NULL,
`JOB_GROUP` VARCHAR(190) DEFAULT NULL,
`IS_NONCONCURRENT` VARCHAR(1) DEFAULT NULL,
`REQUESTS_RECOVERY` VARCHAR(1) DEFAULT NULL,
PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`),
KEY `IDX_QRTZ_FT_TRIG_INST_NAME` (`SCHED_NAME`,`INSTANCE_NAME`),
KEY `IDX_QRTZ_FT_INST_JOB_REQ_RCVRY` (`SCHED_NAME`,`INSTANCE_NAME`,`REQUESTS_RECOVERY`),
KEY `IDX_QRTZ_FT_J_G` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),
KEY `IDX_QRTZ_FT_JG` (`SCHED_NAME`,`JOB_GROUP`),
KEY `IDX_QRTZ_FT_T_G` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
KEY `IDX_QRTZ_FT_TG` (`SCHED_NAME`,`TRIGGER_GROUP`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `QRTZ_JOB_DETAILS` (
`SCHED_NAME` VARCHAR(120) NOT NULL,
`JOB_NAME` VARCHAR(190) NOT NULL,
`JOB_GROUP` VARCHAR(190) 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`),
KEY `IDX_QRTZ_J_REQ_RECOVERY` (`SCHED_NAME`,`REQUESTS_RECOVERY`),
KEY `IDX_QRTZ_J_GRP` (`SCHED_NAME`,`JOB_GROUP`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
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=utf8mb4;
CREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS` (
`SCHED_NAME` VARCHAR(120) NOT NULL,
`TRIGGER_GROUP` VARCHAR(190) NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `QRTZ_SCHEDULER_STATE` (
`SCHED_NAME` VARCHAR(120) NOT NULL,
`INSTANCE_NAME` VARCHAR(190) NOT NULL,
`LAST_CHECKIN_TIME` BIGINT NOT NULL,
`CHECKIN_INTERVAL` BIGINT NOT NULL,
PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `QRTZ_TRIGGERS` (
`SCHED_NAME` VARCHAR(120) NOT NULL,
`TRIGGER_NAME` VARCHAR(190) NOT NULL,
`TRIGGER_GROUP` VARCHAR(190) NOT NULL,
`JOB_NAME` VARCHAR(190) NOT NULL,
`JOB_GROUP` VARCHAR(190) NOT NULL,
`DESCRIPTION` VARCHAR(250) DEFAULT NULL,
`NEXT_FIRE_TIME` BIGINT DEFAULT NULL,
`PREV_FIRE_TIME` BIGINT DEFAULT NULL,
`PRIORITY` INT DEFAULT NULL,
`TRIGGER_STATE` VARCHAR(16) NOT NULL,
`TRIGGER_TYPE` VARCHAR(8) NOT NULL,
`START_TIME` BIGINT NOT NULL,
`END_TIME` BIGINT DEFAULT NULL,
`CALENDAR_NAME` VARCHAR(190) DEFAULT NULL,
`MISFIRE_INSTR` SMALLINT DEFAULT NULL,
`JOB_DATA` BLOB,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
KEY `IDX_QRTZ_T_J` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`),
KEY `IDX_QRTZ_T_JG` (`SCHED_NAME`,`JOB_GROUP`),
KEY `IDX_QRTZ_T_C` (`SCHED_NAME`,`CALENDAR_NAME`),
KEY `IDX_QRTZ_T_G` (`SCHED_NAME`,`TRIGGER_GROUP`),
KEY `IDX_QRTZ_T_STATE` (`SCHED_NAME`,`TRIGGER_STATE`),
KEY `IDX_QRTZ_T_N_STATE` (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),
KEY `IDX_QRTZ_T_N_G_STATE` (`SCHED_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),
KEY `IDX_QRTZ_T_NEXT_FIRE_TIME` (`SCHED_NAME`,`NEXT_FIRE_TIME`),
KEY `IDX_QRTZ_T_NFT_ST` (`SCHED_NAME`,`TRIGGER_STATE`,`NEXT_FIRE_TIME`),
KEY `IDX_QRTZ_T_NFT_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`),
KEY `IDX_QRTZ_T_NFT_ST_MISFIRE` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_STATE`),
KEY `IDX_QRTZ_T_NFT_ST_MISFIRE_GRP` (`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_GROUP`,`TRIGGER_STATE`),
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=utf8mb4;
CREATE TABLE `QRTZ_BLOB_TRIGGERS` (
`SCHED_NAME` VARCHAR(120) NOT NULL,
`TRIGGER_NAME` VARCHAR(190) NOT NULL,
`TRIGGER_GROUP` VARCHAR(190) NOT NULL,
`BLOB_DATA` BLOB,
PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`),
KEY `SCHED_NAME` (`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=utf8mb4;
CREATE TABLE `QRTZ_SIMPLE_TRIGGERS` (
`SCHED_NAME` VARCHAR(120) NOT NULL,
`TRIGGER_NAME` VARCHAR(190) NOT NULL,
`TRIGGER_GROUP` VARCHAR(190) NOT NULL,
`REPEAT_COUNT` BIGINT NOT NULL,
`REPEAT_INTERVAL` BIGINT NOT NULL,
`TIMES_TRIGGERED` BIGINT 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=utf8mb4;
CREATE TABLE `QRTZ_CRON_TRIGGERS` (
`SCHED_NAME` VARCHAR(120) NOT NULL,
`TRIGGER_NAME` VARCHAR(190) NOT NULL,
`TRIGGER_GROUP` VARCHAR(190) NOT NULL,
`CRON_EXPRESSION` VARCHAR(120) 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=utf8mb4;
CREATE TABLE `QRTZ_SIMPROP_TRIGGERS` (
`SCHED_NAME` VARCHAR(120) NOT NULL,
`TRIGGER_NAME` VARCHAR(190) NOT NULL,
`TRIGGER_GROUP` VARCHAR(190) 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 DEFAULT NULL,
`INT_PROP_2` INT DEFAULT NULL,
`LONG_PROP_1` BIGINT DEFAULT NULL,
`LONG_PROP_2` BIGINT 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=utf8mb4;
- 配置 quartz.properties 到项目 resources 下
#调度配置
#调度器实例名称
org.quartz.scheduler.instanceName=SsmScheduler
#调度器实例编号自动生成
org.quartz.scheduler.instanceId=AUTO
#是否在Quartz执行一个job前使用UserTransaction
org.quartz.scheduler.wrapJobExecutionInUserTransaction=false
#线程池配置
#线程池的实现类
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
#线程池中的线程数量
org.quartz.threadPool.threadCount=10
#线程优先级
org.quartz.threadPool.threadPriority=5
#配置是否启动自动加载数据库内的定时任务,默认true
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
#是否设置为守护线程,设置后任务将不会执行
#org.quartz.threadPool.makeThreadsDaemons=true
#持久化方式配置
#JobDataMaps是否都为String类型
org.quartz.jobStore.useProperties=true
#数据表的前缀,默认QRTZ_
org.quartz.jobStore.tablePrefix=QRTZ_
#最大能忍受的触发超时时间
org.quartz.jobStore.misfireThreshold=60000
#是否以集群方式运行
org.quartz.jobStore.isClustered=true
#调度实例失效的检查时间间隔,单位毫秒
org.quartz.jobStore.clusterCheckinInterval=2000
#数据持久化方式,默认存储在内存中,此处使用数据库方式
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
#数据库代理类,一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#数据库别名 随便取
org.quartz.jobStore.dataSource=qzDS
#数据库连接池,将其设置为druid
org.quartz.dataSource.qzDS.connectionProvider.class=com.example.canal.quartz.DruidConnection
#数据库引擎
org.quartz.dataSource.qzDS.driver=com.mysql.jdbc.Driver
#数据库连接
org.quartz.dataSource.qzDS.URL=jdbc:mysql://localhost:3306/yang?useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&useSSL=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
#数据库用户
org.quartz.dataSource.qzDS.user=root
#数据库密码
org.quartz.dataSource.qzDS.password=139926
#允许最大连接
org.quartz.dataSource.qzDS.maxConnection=5
#数据库表信息
#QRTZ_CALENDARS 存储Quartz的Calendar信息
#QRTZ_CRON_TRIGGERS 存储CronTrigger,包括Cron表达式和时区信息
#QRTZ_FIRED_TRIGGERS 存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息
#QRTZ_PAUSED_TRIGGER_GRPS 存储已暂停的Trigger组的信息
#QRTZ_SCHEDULER_STATE 存储少量的有关Scheduler的状态信息,和别的Scheduler实例
#QRTZ_LOCKS 存储程序的悲观锁的信息
#QRTZ_JOB_DETAILS 存储每一个已配置的Job的详细信息
#QRTZ_SIMPLE_TRIGGERS 存储简单的Trigger,包括重复次数、间隔、以及已触的次数
#QRTZ_BLOB_TRIGGERS Trigger作为Blob类型存储
#QRTZ_TRIGGERS 存储已配置的Trigger的信息
#QRTZ_SIMPROP_TRIGGERS
- 创建 QuartzJobFactory
package com.example.canal.quartz.base;
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;
@Component
public class QuartzJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
// 进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
- 创建 quartz 配置类 QuartzConfig
package com.example.canal.quartz.base;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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.scheduling.quartz.SchedulerFactoryBean;
import java.io.IOException;
import java.util.Optional;
@Configuration
public class QuartzConfig {
@Autowired
private QuartzJobFactory jobFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
// 获取配置属性
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
// 创建SchedulerFactoryBean
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setQuartzProperties(propertiesFactoryBean.getObject());
factory.setJobFactory(jobFactory);// 支持在JOB实例中注入其他的业务对象
factory.setApplicationContextSchedulerContextKey("applicationContextKey");
factory.setWaitForJobsToCompleteOnShutdown(true);// 这样当spring关闭时,会等待所有已经启动的quartz job结束后spring才能完全shutdown。
factory.setOverwriteExistingJobs(false);// 是否覆盖己存在的Job
factory.setStartupDelay(10);// QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动
return factory;
}
/**
* 通过SchedulerFactoryBean获取Scheduler的实例
* @return
* @throws IOException
*/
@Bean(name = "scheduler")
public Scheduler scheduler() throws IOException {
return Optional.ofNullable(schedulerFactoryBean().getScheduler()).orElse(null);
}
}
- 创建数据库连接池类
package com.example.canal.quartz;
import com.alibaba.druid.pool.DruidDataSource;
import org.quartz.SchedulerException;
import org.quartz.utils.ConnectionProvider;
import java.sql.Connection;
import java.sql.SQLException;
public class DruidConnection implements ConnectionProvider {
// JDBC驱动
private String driver;
// JDBC连接串
private String URL;
// 数据库用户名
private String user;
// 数据库用户密码
private String password;
// 数据库最大连接数
private int maxConnection;
// 数据库SQL查询每次连接返回执行到连接池,以确保它仍然是有效的。
private String validationQuery;
private boolean validateOnCheckout;
private int idleConnectionValidationSeconds;
// public String maxCachedStatementsPerConnection;
private String discardIdleConnectionsSeconds;
private static final int DEFAULT_DB_MAX_CONNECTIONS = 10;
public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120;
// Druid连接池
private DruidDataSource datasource;
@Override
public Connection getConnection() throws SQLException {
return datasource.getConnection();
}
@Override
public void shutdown() throws SQLException {
datasource.close();
}
@Override
public void initialize() throws SQLException {
if (this.URL == null) {
throw new SQLException("DBPool could not be created: DB URL cannot be null");
}
if (this.driver == null) {
throw new SQLException("DBPool driver could not be created: DB driver class name cannot be null!");
}
if (this.maxConnection < 0) {
throw new SQLException(
"DBPool maxConnectins could not be created: Max connections must be greater than zero!");
}
datasource = new DruidDataSource();
try {
datasource.setDriverClassName(this.driver);
} catch (Exception e) {
try {
throw new SchedulerException("Problem setting driver class name on datasource: " + e.getMessage(), e);
} catch (SchedulerException e1) {
}
}
datasource.setUrl(this.URL);
datasource.setUsername(this.user);
datasource.setPassword(this.password);
datasource.setMaxActive(this.maxConnection);
datasource.setMinIdle(1);
datasource.setMaxWait(0);
datasource.setMaxPoolPreparedStatementPerConnectionSize(DEFAULT_DB_MAX_CONNECTIONS);
if (this.validationQuery != null) {
datasource.setValidationQuery(this.validationQuery);
if (!this.validateOnCheckout)
datasource.setTestOnReturn(true);
else
datasource.setTestOnBorrow(true);
datasource.setValidationQueryTimeout(this.idleConnectionValidationSeconds);
}
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getURL() {
return URL;
}
public void setURL(String URL) {
this.URL = URL;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getMaxConnection() {
return maxConnection;
}
public void setMaxConnection(int maxConnection) {
this.maxConnection = maxConnection;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public boolean isValidateOnCheckout() {
return validateOnCheckout;
}
public void setValidateOnCheckout(boolean validateOnCheckout) {
this.validateOnCheckout = validateOnCheckout;
}
public int getIdleConnectionValidationSeconds() {
return idleConnectionValidationSeconds;
}
public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) {
this.idleConnectionValidationSeconds = idleConnectionValidationSeconds;
}
public DruidDataSource getDatasource() {
return datasource;
}
public void setDatasource(DruidDataSource datasource) {
this.datasource = datasource;
}
public String getDiscardIdleConnectionsSeconds() {
return discardIdleConnectionsSeconds;
}
public void setDiscardIdleConnectionsSeconds(String discardIdleConnectionsSeconds) {
this.discardIdleConnectionsSeconds = discardIdleConnectionsSeconds;
}
}
- 创建job实体类
package com.example.canal.quartz;
import java.io.Serializable;
import java.util.Map;
public class QuartzJob implements Serializable {
// 任务名称
private String jobName;
// 任务所有组
private String groupName;
// 任务执行类
private String jobClass;
// 任务调度时间表达式
private String cronExpression;
// 附加参数
private Map<String, Object> param;
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public String getJobClass() {
return jobClass;
}
public void setJobClass(String jobClass) {
this.jobClass = jobClass;
}
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
public Map<String, Object> getParam() {
return param;
}
public void setParam(Map<String, Object> param) {
this.param = param;
}
}
- 创建任务类 HelloJob
package com.example.canal.quartz.base;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import java.util.Date;
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) {
System.out.println(new Date() + "hello job");
}
}
- 创建业务 Controller 层
package com.example.canal.quartz.Controller;
import com.example.canal.quartz.QuartzJob;
import com.example.canal.quartz.biz.QuartzJobService;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
@Service
@RequestMapping("/quartz")
public class QuartzJobController {
@Resource
private QuartzJobService quartzJobService;
/**
* @Description: 添加任务
* @Author: yangjj_tc
* @Date: 2021/10/18 11:32
*/
@RequestMapping("/addJob")
public Object addJob(@RequestBody QuartzJob quartzJob) {
quartzJobService.addJob(quartzJob.getJobClass(), quartzJob.getJobName(), quartzJob.getGroupName(),
quartzJob.getCronExpression(), quartzJob.getParam());
return HttpStatus.OK;
}
/**
* @Description: 暂停任务
* @Author: yangjj_tc
* @Date: 2021/10/18 11:33
*/
@RequestMapping("/pauseJob")
public Object pauseJob(@RequestBody QuartzJob quartzJob) {
quartzJobService.pauseJob(quartzJob.getJobName(), quartzJob.getGroupName());
return HttpStatus.OK;
}
/**
* @Description: 恢复任务
* @Author: yangjj_tc
* @Date: 2021/10/18 11:33
*/
@RequestMapping("/resumeJob")
public Object resumeJob(@RequestBody QuartzJob quartzJob) {
quartzJobService.resumeJob(quartzJob.getJobName(), quartzJob.getGroupName());
return HttpStatus.OK;
}
/**
* @Description: 立即运行一次定时任务
* @Author: yangjj_tc
* @Date: 2021/10/18 11:35
*/
@RequestMapping("/runOnce")
public Object runOnce(@RequestBody QuartzJob quartzJob) {
quartzJobService.runOnce(quartzJob.getJobName(), quartzJob.getGroupName());
return HttpStatus.OK;
}
/**
* @Description: 更新任务
* @Author: yangjj_tc
* @Date: 2021/10/18 11:33
*/
@RequestMapping("/updateJob")
public Object updateJob(@RequestBody QuartzJob quartzJob) {
quartzJobService.updateJob(quartzJob.getJobName(), quartzJob.getGroupName(), quartzJob.getCronExpression(),
quartzJob.getParam());
return HttpStatus.OK;
}
/**
* @Description: 删除任务
* @Author: yangjj_tc
* @Date: 2021/10/18 11:33
*/
@RequestMapping("/deleteJob")
public Object deleteJob(@RequestBody QuartzJob quartzJob) {
quartzJobService.deleteJob(quartzJob.getJobName(), quartzJob.getGroupName());
return HttpStatus.OK;
}
/**
* @Description: 启动所有任务
* @Author: yangjj_tc
* @Date: 2021/10/18 11:34
*/
@RequestMapping("/startAllJobs")
public Object startAllJobs() {
quartzJobService.startAllJobs();
return HttpStatus.OK;
}
/**
* @Description: 暂停所有任务
* @Author: yangjj_tc
* @Date: 2021/10/18 11:34
*/
@RequestMapping("/pauseAllJobs")
public Object pauseAllJobs() {
quartzJobService.pauseAllJobs();
return HttpStatus.OK;
}
/**
* @Description: 恢复所有任务
* @Author: yangjj_tc
* @Date: 2021/10/18 11:34
*/
@RequestMapping("/resumeAllJobs")
public Object resumeAllJobs() {
quartzJobService.resumeAllJobs();
return HttpStatus.OK;
}
/**
* @Description: 关闭所有任务
* @Author: yangjj_tc
* @Date: 2021/10/18 11:34
*/
@RequestMapping("/shutdownAllJobs")
public Object shutdownAllJobs() {
quartzJobService.shutdownAllJobs();
return HttpStatus.OK;
}
}
- 创建业务 Service 层
package com.example.canal.quartz.biz;
import java.util.Map;
public interface QuartzJobService {
void addJob(String clazzName, String jobName, String groupName, String cronExp, Map<String, Object> param);
void pauseJob(String jobName, String groupName);
void resumeJob(String jobName, String groupName);
void runOnce(String jobName, String groupName);
void updateJob(String jobName, String groupName, String cronExp, Map<String, Object> param);
void deleteJob(String jobName, String groupName);
void startAllJobs();
void pauseAllJobs();
void resumeAllJobs();
void shutdownAllJobs();
}
实现类
package com.example.canal.quartz.biz;
import com.sun.org.slf4j.internal.Logger;
import com.sun.org.slf4j.internal.LoggerFactory;
import org.quartz.*;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Map;
@Service
public class QuartzJobServiceImpl implements QuartzJobService {
@Resource
private Scheduler scheduler;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void addJob(String clazzName, String jobName, String groupName, String cronExp, Map<String, Object> param) {
try {
// 启动调度器,默认初始化的时候已经启动
// scheduler.start();
// 构建job信息
Class<? extends Job> jobClass = (Class<? extends Job>)Class.forName(clazzName);
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, groupName).build();
// 表达式调度构建器(即任务执行的时间)
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExp);
// 按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger =
TriggerBuilder.newTrigger().withIdentity(jobName, groupName).withSchedule(scheduleBuilder).build();
// 获得JobDataMap,写入数据
if (param != null) {
trigger.getJobDataMap().putAll(param);
}
scheduler.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
logger.error(e.getMessage());
}
}
@Override
public void pauseJob(String jobName, String groupName) {
try {
scheduler.pauseJob(JobKey.jobKey(jobName, groupName));
} catch (SchedulerException e) {
logger.error(e.getMessage());
}
}
@Override
public void resumeJob(String jobName, String groupName) {
try {
scheduler.resumeJob(JobKey.jobKey(jobName, groupName));
} catch (SchedulerException e) {
logger.error(e.getMessage());
}
}
@Override
public void runOnce(String jobName, String groupName) {
try {
scheduler.triggerJob(JobKey.jobKey(jobName, groupName));
} catch (SchedulerException e) {
logger.error(e.getMessage());
}
}
@Override
public void updateJob(String jobName, String groupName, String cronExp, Map<String, Object> param) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, groupName);
CronTrigger trigger = (CronTrigger)scheduler.getTrigger(triggerKey);
if (cronExp != null) {
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExp);
// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
}
// 修改map
if (param != null) {
trigger.getJobDataMap().putAll(param);
}
// 按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
} catch (Exception e) {
logger.error(e.getMessage());
}
}
@Override
public void deleteJob(String jobName, String groupName) {
try {
// 暂停、移除、删除
scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, groupName));
scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, groupName));
scheduler.deleteJob(JobKey.jobKey(jobName, groupName));
} catch (Exception e) {
logger.error(e.getMessage());
}
}
@Override
public void startAllJobs() {
try {
scheduler.start();
} catch (Exception e) {
logger.error(e.getMessage());
}
}
@Override
public void pauseAllJobs() {
try {
scheduler.pauseAll();
} catch (Exception e) {
logger.error(e.getMessage());
}
}
@Override
public void resumeAllJobs() {
try {
scheduler.resumeAll();
} catch (Exception e) {
logger.error(e.getMessage());
}
}
@Override
public void shutdownAllJobs() {
try {
if (!scheduler.isShutdown()) {
// 需谨慎操作关闭scheduler容器
// scheduler生命周期结束,无法再 start() 启动scheduler
scheduler.shutdown(true);
}
} catch (Exception e) {
logger.error(e.getMessage());
}
}
}
测试
调用 addJob 接口
{
"jobName": "hello job",
"groupName": "mysqlGroup",
"jobClass": "com.example.canal.quartz.base.HelloJob",
"cronExpression": "0 0/1 * * * ? ",
"param": {
"param": "world"
}
}
控制台打印定时调度