Quartz与SpringBoot集成使⽤
上次⾃⼰搭建Quartz已经是⼏年前的事了,这次项⽬中需要定时任务,需要⽀持集部署,想到⽐较轻量级的定时任务框架就是Quartz,于是来⼀波。
版本说明
通过搜索引擎很容易到其官⽹,来到Document的页⾯,当前版本是2.2.x。
简单的搭建操作
通过Maven引⼊所需的包:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
spring怎么读取配置通过quartz.properties设置相关配置:
# 线程调度器实例名
org.quartz.scheduler.instanceName = quartzScheduler
# 线程池的线程数,即最多3个任务同时跑
org.quartz.threadPool.threadCount = 3
# 使⽤内存存储任务和触发器等信息
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
定义任务,如MySimpleJob,再初始化Scheduler,最后将任务和出发器注册到Scheduler上:
package ise.quartz.springbootquartzexercise;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
public class QuartzBoot {
private static Logger logger = Logger(QuartzBoot.class);
public static void main(String[] args) {
try {
// 获取调度器
Scheduler scheduler = DefaultScheduler();
// 开启调度器
scheduler.start();
/
/ 注册⼀个⽰例任务和触发器
registerJobAndTrigger(scheduler);
// scheduler.shutdown();
} catch (SchedulerException se) {
<("调度器初始化异常", se);
}
}
/**
* 注册⼀个任务和触发器
*/
public static void registerJobAndTrigger(Scheduler scheduler) {
JobDetail job = wJob(MySimpleJob.class)
.withIdentity("mySimpleJob", "simpleGroup")
.build();
Trigger trigger = org.wTrigger()
.withIdentity("simpleTrigger", "simpleGroup")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(10)
.repeatForever())
.build();
try {
scheduler.scheduleJob(job, trigger);
} catch (SchedulerException e) {
<("注册任务和触发器失败", e);
}
}
/**
* 简单的任务
*/
public static class MySimpleJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.info("哇真的执⾏了");
}
}
}
启动⽇志,你可以看到任务按照指定的触发器跑:
13:31:28.759 [main] INFO org.quartz.impl.StdSchedulerFactory - Using default implementation for ThreadExecutor
13:31:28.828 [main] INFO SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class SchedulerSignalerImpl
13:31:28.828 [main] INFO QuartzScheduler - Quartz Scheduler v.2.2.3 created.
13:31:28.831 [main] INFO org.quartz.simpl.RAMJobStore - RAMJobStore initialized.
13:31:28.833 [main] INFO QuartzScheduler - Scheduler meta-data: Quartz Scheduler (
v2.2.3) 'quartzScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 3 threads.
Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
13:31:28.833 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'quartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
13:31:28.833 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.2.3
13:31:28.834 [main] INFO QuartzScheduler - Scheduler quartzScheduler_$_NON_CL
USTERED started.
13:31:28.834 [quartzScheduler_QuartzSchedulerThread] DEBUG QuartzSchedulerThread - batch acquisition of 0 triggers
13:31:28.853 [quartzScheduler_QuartzSchedulerThread] DEBUG QuartzSchedulerThread - batch acquisition of 1 triggers
13:31:28.860 [quartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'SimpleJob', class=ise.quartz.springbootquartzexercise.QuartzBoot$M 13:31:28.869 [quartzScheduler_QuartzSchedulerThread] DEBUG QuartzSchedulerThread - batch acquisition of 1 triggers
13:31:28.871 [quartzScheduler_Worker-1] DEBUG JobRunShell - Calling execute on SimpleJob
13:31:28.871 [quartzScheduler_Worker-1] INFO ise.quartz.springbootquartzexercise.QuartzBoot - 哇真的执⾏了
13:31:38.842 [quartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'SimpleJob', class=ise.quartz.springbootquartzexercise.QuartzBoot$M 13:31:38.842 [quartzScheduler_Worker-2] DEBUG JobRunShell - Calling execute on SimpleJob
13:31:38.842 [quartzScheduler_Worker-2] INFO ise.quartz.springbootquartzexercise.QuartzBoot - 哇真的执⾏了
扩展
查看DefaultScheduler(),会发现⽤的是new StdSchedulerFactory(),在initialize() ⽅法可以看到默认从指定配置的⽂件或quartz.properties读取配置:
String requestedFile = Property("org.quartz.properties");
String propFileName = requestedFile != null ? requestedFile : "quartz.properties";
调度器待机状态、关闭状态
在调⽤scheduler.start()启动调度器后,可以使⽤scheduler.standby();将调度器转为待机状态,此状态下任务和触发器不会被触发。
另外,可以使⽤scheduler.shutdown()关闭调度器,是不可逆的,即调⽤后是不可以重新开始的。
它的参数不同,意义有所不同:
scheduler.shutdown()=scheduler.shutdown(false),⽅法会马上返回,正在执⾏的任务会继续执⾏
scheduler.shutdown(true),知道正在执⾏的任务执⾏完成才返回
在getScheduler()可以看到使⽤SchedulerRepository缓存了Scheduler,使⽤的是HashMap<String, Scheduler>:
SchedulerRepository schedRep = Instance();
Scheduler sched = schedRep.SchedulerName());
与Spring Boot集成
⾸先,quartz.properties与之前的基本⼀致:
# 线程调度器实例名
org.quartz.scheduler.instanceName = quartzScheduler
# 线程池的线程数,即最多3个任务同时跑
org.quartz.threadPool.threadCount = 3
# 使⽤内存存储任务和触发器等信息
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
因会与Spring Scheduling集成,我们按照要求定义任务,需要特别注意的是@Component和@EnableScheduling:
package ise.quartz.springbootquartzexerciseponent.quartz.job;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;
@Component
@EnableScheduling
public class MyFirstExerciseJob {
private Logger logger = Class());
public void myJobBusinessMethod() {
this.logger.info("哇被触发了哈哈哈哈哈");
}
}
类似的⽅式,可以定义MySecondExerciseJob。
注册任务和触发器:
package ise.quartz.springbootquartzexerciseponent.quartz;
import ise.quartz.springbootquartzexerciseponent.quartz.job.MyFirstExerciseJob;
import ise.quartz.springbootquartzexerciseponent.quartz.job.MySecondExerciseJob;
import org.springframework.beans.factory.annotation.Qualifier;
import t.annotation.Bean;
import t.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
@Configuration
public class QuartzJobConfig {
/**
* ⽅法调⽤任务明细⼯⼚Bean
*/
@Bean(name = "myFirstExerciseJobBean")
public MethodInvokingJobDetailFactoryBean myFirstExerciseJobBean(MyFirstExerciseJob myFirstExerciseJob) {
MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();
jobDetail.setConcurrent(false); // 是否并发
jobDetail.setName("general-myFirstExerciseJob"); // 任务的名字
jobDetail.setGroup("general"); // 任务的分组
jobDetail.setTargetObject(myFirstExerciseJob); // 被执⾏的对象
jobDetail.setTargetMethod("myJobBusinessMethod"); // 被执⾏的⽅法
return jobDetail;
}
/**
* 表达式触发器⼯⼚Bean
*/
@Bean(name = "myFirstExerciseJobTrigger")
public CronTriggerFactoryBean myFirstExerciseJobTrigger(@Qualifier("myFirstExerciseJobBean") MethodInvokingJobDetailFactoryBean myFirstExerciseJobBean) {
CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();
tigger.Object());
tigger.setCronExpression("0/10 * * * * ?"); // 什么是否触发,Spring Scheduler Cron表达式
tigger.setName("general-myFirstExerciseJobTrigger");
return tigger;
}
/**
* ⽅法调⽤任务明细⼯⼚Bean
*/
@Bean(name = "mySecondExerciseJobBean")
public MethodInvokingJobDetailFactoryBean mySecondExerciseJobBean(MySecondExerciseJob mySecondExerciseJob) {
MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();
jobDetail.setConcurrent(false); // 是否并发
jobDetail.setName("general-mySecondExerciseJob"); // 任务的名字
jobDetail.setGroup("general"); // 任务的分组
jobDetail.setTargetObject(mySecondExerciseJob); // 被执⾏的对象
jobDetail.setTargetMethod("myJobBusinessMethod"); // 被执⾏的⽅法
return jobDetail;
}
/**
* 表达式触发器⼯⼚Bean
*/
@Bean(name = "mySecondExerciseJobTrigger")
public CronTriggerFactoryBean mySecondExerciseJobTrigger(@Qualifier("mySecondExerciseJobBean") MethodInvokingJobDetailFactoryBean mySecondExerciseJobDetailFactoryBean) {
CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();
tigger.Object());
tigger.setCronExpression("0/10 * * * * ?"); // 什么是否触发,Spring Scheduler Cron表达式
tigger.setName("general-mySecondExerciseJobTrigger");
return tigger;
}
}
将任务和触发器注册到调度器:
package ise.quartz.springbootquartzexerciseponent.quartz;
import org.quartz.Trigger;
import org.springframework.beans.factory.annotation.Qualifier;
import t.annotation.Bean;
import t.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@Configuration
public class QuartzConfig {
/**
* 调度器⼯⼚Bean
*/
@Bean(name = "schedulerFactory")
public SchedulerFactoryBean schedulerFactory(@Qualifier("myFirstExerciseJobTrigger") Trigger myFirstExerciseJobTrigger,
@Qualifier("mySecondExerciseJobTrigger") Trigger mySecondExerciseJobTrigger) {
SchedulerFactoryBean bean = new SchedulerFactoryBean();
// 覆盖已存在的任务
bean.setOverwriteExistingJobs(true);
// 延时启动定时任务,避免系统未完全启动却开始执⾏定时任务的情况
bean.setStartupDelay(15);
// 注册触发器
bean.setTriggers(myFirstExerciseJobTrigger, mySecondExerciseJobTrigger);
return bean;
}
}
持久化
任务持久化需要⽤到数据库,⽽初始化数据库的SQL可以从下载的发布版的⽂件中到,⽐如,我在官
⽹的Download页下载了当前版本的Full Distribution:Quartz 2.2.3 .,解压后在quartz-2.2.3\docs\dbTables能到初始化脚本,因我⽤的是MySQL的Innodb引擎,所以我⽤此脚本tables_mysql_innodb.sql。
不能持久化的问题
如果使⽤的是MethodInvokingJobDetailFactoryBean,持久化会有如下报错:Couldn't store job: Unable to serialize JobDataMap for insertion into database because the value of ,我们切换使
⽤JobDetailFactoryBean。
简单的持久化⽰例
quartz.properties的配置有所不同,⽐如会使⽤JobStoreTX,然后会指定数据源的信息:
# 线程调度器实例名
org.quartz.scheduler.instanceName = quartzScheduler
# 线程池的线程数,即最多3个任务同时跑
org.quartz.threadPool.threadCount = 3
# 如何存储任务和触发器等信息
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.dataSource = quartzDataSource
# 是否集
org.quartz.jobStore.isClustered = false
# 数据源
# 驱动
org.quartz.dataSource.quartzDataSource.driver = sql.cj.jdbc.Driver
# 连接URL
org.quartz.dataSource.quartzDataSource.URL = jdbc:mysql://localhost:3306/quartz?characterEncoding=utf-8&useSSL=true&&serverTimezone=Asia/Shanghai
# ⽤户名
org.quartz.dataSource.quartzDataSource.user = root
# 密码
org.quartz.dataSource.quartzDataSource.password = 123456
# 最⼤连接数
org.quartz.dataSource.quartzDataSource.maxConnections = 5
定义的任务有所不同:
package ise.quartz.springbootquartzexerciseponent.quartz.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;
@Component
@EnableScheduling
public class MyFirstExerciseJob implements Job {
private Logger logger = Class());
@Override
public void execute(JobExecutionContext jobExecutionContext) {
}
public void myJobBusinessMethod() {
this.logger.info("哇被触发了哈哈哈哈哈");
}
}
注册任务和触发器的⽅式有所不同:
/
**
* ⽅法调⽤任务明细⼯⼚Bean
*/
@Bean(name = "myFirstExerciseJobBean")
public JobDetailFactoryBean myFirstExerciseJobBean() {
JobDetailFactoryBean jobDetail = new JobDetailFactoryBean();
jobDetail.setName("general-myFirstExerciseJob"); // 任务的名字
jobDetail.setGroup("general"); // 任务的分组
jobDetail.setJobClass(MyFirstExerciseJob.class);
jobDetail.setDurability(true);
return jobDetail;
}
/**
* 表达式触发器⼯⼚Bean
*/
@Bean(name = "myFirstExerciseJobTrigger")
public CronTriggerFactoryBean myFirstExerciseJobTrigger(@Qualifier("myFirstExerciseJobBean") JobDetailFactoryBean myFirstExerciseJobBean) {
CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();
tigger.Object());
tigger.setCronExpression("0/10 * * * * ?"); // 什么是否触发,Spring Scheduler Cron表达式
tigger.setName("general-myFirstExerciseJobTrigger");
return tigger;
}
注册所有任务和触发器:
package ise.quartz.springbootquartzexerciseponent.quartz;
import org.quartz.Trigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import t.annotation.Bean;
import t.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;
@Configuration
public class QuartzConfig {
private Logger logger = Class());
@Autowired
@Qualifier(value = "primaryDataSource")
private DataSource primaryDataSource;
/**
* 调度器⼯⼚Bean
*/
@Bean(name = "schedulerFactory")
public SchedulerFactoryBean schedulerFactory( triggers) {
SchedulerFactoryBean bean = new SchedulerFactoryBean();
Properties p = new Properties();
try {
p.Class().getClassLoader().getResourceAsStream("quartz.properties"));
} catch (IOException e) {
("加载quartz.properties失败", e);
throw new Error(e);
}
bean.setQuartzProperties(p);
// 覆盖已存在的任务
bean.setOverwriteExistingJobs(true);
// 延时启动定时任务,避免系统未完全启动却开始执⾏定时任务的情况
bean.setStartupDelay(15);
// 注册触发器
bean.setTriggers(triggers);
return bean;
}
}
使⽤应⽤的数据源
有时候持久化的数据源⽤的是应⽤的数据源,可以使⽤bean.setDataSource(dataSource)设置或覆盖数据源。
⽐如,应⽤的数据源是这样的。
POM.XML:
<!-- 引⼊数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
<!-- Spring Boot JDBC -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
数据源配置:
spring.pe = com.alibaba.druid.pool.DruidDataSource
spring.dataSource.primaryDataSource.url = jdbc:mysql://localhost:33306/quartz?characterEncoding=utf-8&useSSL=true&&serverTimezone=Asia/Shanghai&allowMultiQueries=true&autoReconnect=true spring.dataSource.primaryDataSource.username = root
spring.dataSource.primaryDataSource.password = 123456
spring.dataSource.primaryDataSource.driverClassName = sql.jdbc.Driver
spring.dataSource.primaryDataSource.initialSize = 5
spring.dataSource.primaryDataSource.minIdle = 5
spring.dataSource.primaryDataSource.maxActive = 15
spring.dataSource.primaryDataSource.maxWait = 60000
spring.dataSource.primaryDataSource.timeBetweenEvictionRunsMillis = 60000
spring.dataSource.primaryDataSource.minEvictableIdleTimeMillis = 300000
spring.dataSource.primaryDataSource.validationQuery = SELECT 1 FROM DUAL
spring.stWhileIdle = true
spring.stOnBorrow = true
spring.stOnReturn = true
配置载⼊类:
package ise.quartz.springbootquartzexerciseponent.datasourcepool;
import org.t.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "spring.dataSource.primaryDataSource")
public class DruidPrimaryDataSourceConfigProperties {
private String type;
private String url;
private String username;
private String password;
private String driverClassName;
private Integer initialSize;
private Integer minIdle;
private Integer maxActive;
private Integer maxWait;
private Integer timeBetweenEvictionRunsMillis;
private Integer minEvictableIdleTimeMillis;
private String validationQuery;
private Boolean testWhileIdle;
private Boolean testOnBorrow;
private Boolean testOnReturn;
public String getType() {
return type;
}
public void setType(String type) {
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public Integer getInitialSize() {
return initialSize;
}
public void setInitialSize(Integer initialSize) {
this.initialSize = initialSize;
}
public Integer getMinIdle() {
return minIdle;
}
public void setMinIdle(Integer minIdle) {
this.minIdle = minIdle;
}
public Integer getMaxActive() {
return maxActive;
}
public void setMaxActive(Integer maxActive) {
this.maxActive = maxActive;
}
public Integer getMaxWait() {
return maxWait;
}
public void setMaxWait(Integer maxWait) {
this.maxWait = maxWait;
}
public Integer getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
public void setTimeBetweenEvictionRunsMillis(Integer timeBetweenEvictionRunsMillis) { this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
public Integer getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
public void setMinEvictableIdleTimeMillis(Integer minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
public String getValidationQuery() {
return validationQuery;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论