注:本文介绍的mysql版本是5.x ,如果mysql升级到8 有几个地方需要改动
https://blog.csdn.net/a704397849/article/details/108396563
本文是在 csdn博主 Eddie-Wang 的文章 《springboot整合quartz》 基础上做了部分修改 增加了数据库操作和配置, 最后提供了demo
简介
Quartz是一款功能强大的任务调度器,可以实现较为复杂的调度功能,如每月一号执行、每天凌晨执行、每周五执行等等,还支持分布式调度。本文使用Springboot+Mybatis+Quartz实现对定时任务的增、删、改、查、启用、停用等功能。并把定时任务持久化到数据库以及支持集群。
Quartz的3个基本要素
1.Scheduler:调度器。所有的调度都是由它控制。
2.Trigger: 触发器。决定什么时候来执行任务。
3.JobDetail & Job: JobDetail定义的是任务数据,而真正的执行逻辑是在Job中。使用JobDetail + Job而不是Job,这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。
springboot集成Quartz
Quartz持久化需要用到数据库,这里使用的是mysql 需要在Quartz官网下载Quartz mysql版的sql文件 tables_mysql_innodb.sql ,执行创建一些必要的Quartz表。下载地址,由于下面介绍的是 Quartz 2.2.3版本 ,所以下载 quartz-2.2.3-distribution.tar.gz,解压后在 quartz-2.2.3-distribution\quartz-2.2.3\docs\dbTables 下可以找到 tables_mysql_innodb.sql
在这里我直接贴出 tables_mysql_innodb.sql 的内容,就不用去下载这个文件了
#
# In your Quartz properties file, you'll need to set
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#
#
# By: Ron Cordell - roncordell
# I didn't see this anywhere, so I thought I'd post it here. This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM.
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_LOCKS;
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_JOB_DETAILS(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) 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 NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;
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) NULL,
NEXT_FIRE_TIME BIGINT(13) NULL,
PREV_FIRE_TIME BIGINT(13) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(13) NOT NULL,
END_TIME BIGINT(13) NULL,
CALENDAR_NAME VARCHAR(200) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
ENGINE=InnoDB;
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),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
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(120) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
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) NULL,
STR_PROP_2 VARCHAR(512) NULL,
STR_PROP_3 VARCHAR(512) NULL,
INT_PROP_1 INT NULL,
INT_PROP_2 INT NULL,
LONG_PROP_1 BIGINT NULL,
LONG_PROP_2 BIGINT NULL,
DEC_PROP_1 NUMERIC(13,4) NULL,
DEC_PROP_2 NUMERIC(13,4) NULL,
BOOL_PROP_1 VARCHAR(1) NULL,
BOOL_PROP_2 VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
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 NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
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;
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;
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 INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(200) NULL,
JOB_GROUP VARCHAR(200) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;
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;
CREATE TABLE QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;
CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
commit;
在mysql中执行这个文件 创建 Quartz 持久化需要的表。 我这里使用mysql图形客户端nacicat 新建了一个quartz数据库,并在这个数据库下面执行了tables_mysql_innodb.sql 文件会生成多个QRTZ_开头的表 ,如下:
注:如果生成的表名是小写的,那是数据库配置设置了不区分大小写.
下面开始代码整合
工程目录结构如下:
各个文件简介及内容
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>quartz_demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>quartz_demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--quartz-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.3</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!--数据库连接池druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties 文件内容空,服务端口默认8080
quartz.properties 是quartz的配置文件
org.quartz.scheduler.instanceName=quartzScheduler
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.rmi.export=false
org.quartz.scheduler.rmi.proxy=false
org.quartz.scheduler.wrapJobExecutionInUserTransaction=false
org.quartz.jobStore.misfireThreshold=50000
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.useProperties=false
org.quartz.jobStore.clusterCheckinInterval=20000
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=10
org.quartz.threadPool.threadPriority=5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
dao.properties 数据库连接配置文件
#quartz持久化相关表的数据库连接信息,根据自己实际环境做更改
spring.datasource.test.jdbcUrl=jdbc:mysql://127.0.0.1:3306/quartz?useUnicode=true&characterEncoding=UTF-8&useSSL=false&&autoReconnect=true&failOverReadOnly=false
spring.datasource.test.username=root
spring.datasource.test.password=123456
spring.datasource.test.driver-class-name=com.mysql.jdbc.Driver
#连接池的配置信息
spring.datasource.druid.initialSize=5
spring.datasource.druid.minIdle=5
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=60000
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.minEvictableIdleTimeMillis=300000
spring.datasource.druid.validationQuery=SELECT 1 FROM DUAL
spring.datasource.druid.testWhileIdle=true
spring.datasource.druid.testOnBorrow=false
spring.datasource.druid.testOnReturn=false
spring.datasource.druid.poolPreparedStatements=true
spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.druid.filters=stat,wall
DataSourceQuartzConfig 数据库连接配置类
package com.example.quartz_demo.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
/**
* @author
*/
@Configuration
@PropertySource("classpath:dao.properties")
public class DataSourceQuartzConfig {
@Value("${spring.datasource.test.jdbcUrl}")
private String url;
@Value("${spring.datasource.test.username}")
private String user;
@Value("${spring.datasource.test.password}")
private String password;
@Value("${spring.datasource.test.driver-class-name}")
private String driverClass;
@Value("${spring.datasource.druid.maxActive}")
private Integer maxActive;
@Value("${spring.datasource.druid.minIdle}")
private Integer minIdle;
@Value("${spring.datasource.druid.initialSize}")
private Integer initialSize;
@Value("${spring.datasource.druid.maxWait}")
private Long maxWait;
@Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
private Long timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
private Long minEvictableIdleTimeMillis;
@Value("${spring.datasource.druid.testWhileIdle}")
private Boolean testWhileIdle;
@Value("${spring.datasource.druid.testWhileIdle}")
private Boolean testOnBorrow;
@Value("${spring.datasource.druid.testOnBorrow}")
private Boolean testOnReturn;
@Bean(name = "quartzDataSource")
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClass);
dataSource.setUrl(url);
dataSource.setUsername(user);
dataSource.setPassword(password);
//连接池配置
dataSource.setMaxActive(maxActive);
dataSource.setMinIdle(minIdle);
dataSource.setInitialSize(initialSize);
dataSource.setMaxWait(maxWait);
dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
dataSource.setTestWhileIdle(testWhileIdle);
dataSource.setTestOnBorrow(testOnBorrow);
dataSource.setTestOnReturn(testOnReturn);
dataSource.setValidationQuery("SELECT 'x'");
dataSource.setPoolPreparedStatements(true);
dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
return dataSource;
}
}
AutowiringSpringBeanJobFactory 任务实例化
package com.example.quartz_demo.job;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
/**
* @author:
* @create: 2019-10-29 21:28
**/
public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory
implements ApplicationContextAware
{
private transient AutowireCapableBeanFactory beanFactory;
@Override
public void setApplicationContext(final ApplicationContext context)
{
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle)
throws Exception
{
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
SchedulerConfig 对调度器做一些配置,将之前的数据源配置,jobfactory都注入spring ioc
注:里面有一个静态方式配置定时任务的案例
package com.example.quartz_demo.job;
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.quartz.spi.JobFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;
/**
* @author:
* @create: 2019-10-29 21:29
**/
@EnableScheduling
@Configuration
public class SchedulerConfig
{
@Autowired
@Qualifier("quartzDataSource")
DataSource dataSource;
@Bean
public JobFactory jobFactory(ApplicationContext applicationContext)
{
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory,
Trigger simpleJobTrigger)
throws IOException
{
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
factory.setTriggers(simpleJobTrigger);
factory.setDataSource(dataSource);
factory.setWaitForJobsToCompleteOnShutdown(
true);//这样当spring关闭时,会等待所有已经启动的quartz job结束后spring才能完全shutdown。
factory.setOverwriteExistingJobs(false);
factory.setStartupDelay(1);
//设置调度器自动运行
factory.setAutoStartup(true);
//设置上下文spring bean name
factory.setApplicationContextSchedulerContextKey("applicationContext");
return factory;
}
/**
* 静态方式配置定时任务
*
* @param jobDetail
* @return
*/
@Bean
public CronTriggerFactoryBean simpleJobTrigger(
@Qualifier("simpleJobDetail") JobDetail jobDetail)
{
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail);
factoryBean.setStartDelay(1000L);
factoryBean.setName("trigger1");
factoryBean.setGroup("group1");
//周1至周5,每天上午8点至下午18点,每分钟执行一次
factoryBean.setCronExpression("0 0/1 8-18 ? * 2-6");
return factoryBean;
}
@Bean
public JobDetailFactoryBean simpleJobDetail()
{
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(ScheduledJob.class);
factoryBean.setGroup("group1");
factoryBean.setName("job1");
return factoryBean;
}
@Bean
public Properties quartzProperties()
throws IOException
{
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
}
SchedulerListener 是 quartz 监听器,用于在任务执行前后做一些操作
package com.example.quartz_demo.job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
/**
* @author:
* @create: 2019-10-29 21:55
**/
public class SchedulerListener implements JobListener
{
public static final String LISTENER_NAME = "QuartSchedulerListener";
@Override
public String getName() {
return LISTENER_NAME; //must return a name
}
//任务被调度前
@Override
public void jobToBeExecuted(JobExecutionContext context) {
String jobName = context.getJobDetail().getKey().toString();
System.out.println("jobToBeExecuted");
System.out.println("Job : " + jobName + " is going to start...");
}
//任务调度被拒了
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
System.out.println("jobExecutionVetoed");
//可以做一些日志记录原因
}
//任务被调度后
@Override
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException)
{
System.out.println("jobWasExecuted");
String jobName = context.getJobDetail().getKey().toString();
System.out.println("Job : " + jobName + " is finished...");
if (jobException != null && !jobException.getMessage().equals("")){
System.out.println("Exception thrown by: " + jobName
+ " Exception: " + jobException.getMessage());
}
}
}
ScheduledJob 定义一个用于实现具体的业务逻辑 的 具体job
package com.example.quartz_demo.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
/**
* @author:
* @create: 2019-10-29 21:32
**/
public class ScheduledJob implements Job
{
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException{
String jobName = jobExecutionContext.getJobDetail().getKey().getName();
String jobGroup = jobExecutionContext.getJobDetail().getKey().getGroup();
String content = jobExecutionContext.getJobDetail().getJobDataMap().getString("content");
//执行任务逻辑....
System.out.println("执行自定义定时任务, time is {}." + new Date() +
" ,job name = " + jobName + " ,group = "+ jobGroup +
" ,content = " + content);
}
}
SchedulerManager 是用于实现任务的创建,删除等操作的管理类
package com.example.quartz_demo.job;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;
/**
* @author:
* @create: 2019-10-29 21:56
**/
@Component
public class SchedulerManager
{
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;
private JobListener scheduleListener;
/**
* 开始定时任务
*
* @param jobName
* @param jobGroup
* @throws SchedulerException
*/
public void startJob(String cron, String jobName, String jobGroup,String content,
Class<? extends Job> jobClass)
throws SchedulerException
{
Scheduler scheduler = schedulerFactoryBean.getScheduler();
if (scheduleListener == null)
{
scheduleListener = new SchedulerListener();
scheduler.getListenerManager().addJobListener(scheduleListener);
}
JobKey jobKey = new JobKey(jobName, jobGroup);
if (!scheduler.checkExists(jobKey))
{
scheduleJob(cron, scheduler, jobName, jobGroup, content, jobClass);
}
}
/**
* 移除定时任务
*
* @param jobName
* @param jobGroup
* @throws SchedulerException
*/
public void deleteJob(String jobName, String jobGroup)
throws SchedulerException
{
Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = new JobKey(jobName, jobGroup);
scheduler.deleteJob(jobKey);
}
/**
* 暂停定时任务
*
* @param jobName
* @param jobGroup
* @throws SchedulerException
*/
public void pauseJob(String jobName, String jobGroup)
throws SchedulerException
{
Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey jobKey = new JobKey(jobName, jobGroup);
scheduler.pauseJob(jobKey);
}
/**
* 恢复定时任务
*
* @param jobName
* @param jobGroup
* @throws SchedulerException
*/
public void resumeJob(String jobName, String jobGroup)
throws SchedulerException
{
Scheduler scheduler = schedulerFactoryBean.getScheduler();
JobKey triggerKey = new JobKey(jobName, jobGroup);
scheduler.resumeJob(triggerKey);
}
/**
* 清空所有当前scheduler对象下的定时任务【目前只有全局一个scheduler对象】
*
* @throws SchedulerException
*/
public void clearAll()
throws SchedulerException
{
Scheduler scheduler = schedulerFactoryBean.getScheduler();
scheduler.clear();
}
/**
* 动态创建Job
* 此处的任务可以配置可以放到properties或者是放到数据库中
* Trigger:name和group 目前和job的name、group一致,之后可以扩展归类
*
* @param scheduler
* @throws SchedulerException
*/
private void scheduleJob(String cron, Scheduler scheduler, String jobName, String jobGroup,String content,
Class<? extends Job> jobClass)
throws SchedulerException
{
/*
* 此处可以先通过任务名查询数据库,如果数据库中存在该任务,更新任务的配置以及触发器
* 如果此时数据库中没有查询到该任务,则按照下面的步骤新建一个任务,并配置初始化的参数,并将配置存到数据库中
*/
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).build();
jobDetail.getJobDataMap().put("content",content);
//
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName,
jobGroup).withSchedule(scheduleBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
}
}
QuartzController 是用于体验quartz 发出、关闭定时任务功能 的web controller
package com.example.quartz_demo.controller;
import com.example.quartz_demo.job.ScheduledJob;
import com.example.quartz_demo.job.SchedulerManager;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* @author:
* @create: 2019-10-30 15:58
**/
@RestController
public class QuartzController {
@Autowired
public SchedulerManager myScheduler;
@RequestMapping(value = "/quartz/job2", method = RequestMethod.GET)
public String scheduleJob2() {
System.out.println("/quartz/job2");
try {
String content = "这是一个content";
//每五秒执行一次
myScheduler.startJob("0/5 * * * * ?", "job2", "group2", content, ScheduledJob.class);
return "启动定时器成功";
} catch (SchedulerException e) {
e.printStackTrace();
}
return "启动定时器失败";
}
@RequestMapping(value = "/quartz/del_job2", method = RequestMethod.GET)
public String deleteScheduleJob2() {
System.out.println("/quartz/del_job2");
try {
myScheduler.deleteJob("job2", "group2");
return "删除定时器成功";
} catch (SchedulerException e) {
e.printStackTrace();
}
return "删除定时器失败";
}
}
测试
启动程序后,在浏览器输入 http://127.0.0.1:8080/quartz/job2
开始任务,每隔5秒会触发一次任务
浏览器输入 http://127.0.0.1:8080/quartz/del_job2
结束任务,任务不再触发
源码demo
链接:https://pan.baidu.com/s/1DUbcf2AIm35BKEU-fikLXQ
提取码:c3th
注:别忘了 代码中数据库地址替换成自己可访问的mysql数据库连接地址,我这里quartz相关持久化表是在数据库quartz中,如果想要放别的数据库,自行更改。
补充一个小工具
我们在项目中可能会有这种需求:设置提醒任务,比如设置"2019-11-11 11:11:11" 提醒做某事
用下面工具可以快速将 “yyyy-MM-dd HH:mm:ss” 格式的时间快速转为cron 表达式字符串: TimeUtil.getCron(TimeUtil.yMdHms2Date(“2019-10-10 11:11:11” );
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author
*/
public class TimeUtil {
/**
* yyyy-MM-dd HH:mm:ss 时间字符串 转 Date
* @param s
* @return
*/
public static Date yMdHms2Date(String s){
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = simpleDateFormat.parse(s);
return date;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
/**
* 日期转化为cron表达式
* @param date
* @return
*/
public static String getCron(java.util.Date date){
String dateFormat="ss mm HH dd MM ? yyyy";
return fmtDateToStr(date, dateFormat);
}
/**
*
* @param date
* @param dtFormat
* @return
*/
public static String fmtDateToStr(Date date, String dtFormat) {
if (date == null) {
return "";
}
try {
SimpleDateFormat dateFormat = new SimpleDateFormat(dtFormat);
return dateFormat.format(date);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
}