SpringBoot2.0.3整合Quartz2.3.0实现定时任务
转载:https://www.cnblogs.com/ealenxie/p/9134602.html
关于别人写的quartz学习的地址:https://blog.csdn.net/lkl_csdn/article/details/73613033,原理我就不讲了,直接上代码。
因为想要做一个类似于调度中心的东西,定时执行一些Job(通常是一些自定义程序或者可执行的jar包),搭了一个例子,总结了前辈们的相关经验和自己的一些理解,如有雷同或不当之处,望各位大佬见谅和帮忙指正。
1.首先新建项目SpringBoot-Quartz ,选用的技术栈为 SpringBoot + Quartz + Spring Data Jpa。完整代码可见 : https://github.com/EalenXie/SpringBoot-Quartz
pom.xml依赖 :
1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xmlns="http://maven.apache.org/POM/4.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5 <modelVersion>4.0.0</modelVersion>
6 <groupId>name.ealenxie</groupId>
7 <artifactId>SpringBoot-Quartz</artifactId>
8 <version>1.0</version>
9 <parent>
10 <groupId>org.springframework.boot</groupId>
11 <artifactId>spring-boot-starter-parent</artifactId>
12 <version>2.0.1.RELEASE</version>
13 </parent>
14 <dependencies>
15 <dependency>
16 <groupId>org.springframework.boot</groupId>
17 <artifactId>spring-boot-starter-data-jpa</artifactId>
18 </dependency>
19 <dependency>
20 <groupId>org.springframework.boot</groupId>
21 <artifactId>spring-boot-starter-web</artifactId>
22 </dependency>
23 <dependency>
24 <groupId>mysql</groupId>
25 <artifactId>mysql-connector-java</artifactId>
26 <scope>runtime</scope>
27 </dependency>
28 <dependency> <!--quartz依赖-->
29 <groupId>org.quartz-scheduler</groupId>
30 <artifactId>quartz</artifactId>
31 <version>2.2.3</version>
32 </dependency>
33 <dependency>
34 <groupId>org.springframework</groupId>
35 <artifactId>spring-context-support</artifactId>
36 </dependency>
37 <dependency>
38 <groupId>org.quartz-scheduler</groupId>
39 <artifactId>quartz-jobs</artifactId>
40 <version>2.2.3</version>
41 </dependency>
42 </dependencies>
43 </project>
2.你需要在数据库中建立Quartz的相关系统表,所以你需要在数据库中执行如下来自Quartz官方的脚本。
1 -- in your Quartz properties file, you'll need to set org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
2 -- 你需要在你的quartz.properties文件中设置org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
3 -- StdJDBCDelegate说明支持集群,所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务
4 -- This is the script from Quartz to create the tables in a MySQL database, modified to use INNODB instead of MYISAM
5 -- 这是来自quartz的脚本,在MySQL数据库中创建以下的表,修改为使用INNODB而不是MYISAM
6 -- 你需要在数据库中执行以下的sql脚本
7 DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
8 DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
9 DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
10 DROP TABLE IF EXISTS QRTZ_LOCKS;
11 DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
12 DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
13 DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
14 DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
15 DROP TABLE IF EXISTS QRTZ_TRIGGERS;
16 DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
17 DROP TABLE IF EXISTS QRTZ_CALENDARS;
18 -- 存储每一个已配置的Job的详细信息
19 CREATE TABLE QRTZ_JOB_DETAILS(
20 SCHED_NAME VARCHAR(120) NOT NULL,
21 JOB_NAME VARCHAR(200) NOT NULL,
22 JOB_GROUP VARCHAR(200) NOT NULL,
23 DESCRIPTION VARCHAR(250) NULL,
24 JOB_CLASS_NAME VARCHAR(250) NOT NULL,
25 IS_DURABLE VARCHAR(1) NOT NULL,
26 IS_NONCONCURRENT VARCHAR(1) NOT NULL,
27 IS_UPDATE_DATA VARCHAR(1) NOT NULL,
28 REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
29 JOB_DATA BLOB NULL,
30 PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP))
31 ENGINE=InnoDB;
32 -- 存储已配置的Trigger的信息
33 CREATE TABLE QRTZ_TRIGGERS (
34 SCHED_NAME VARCHAR(120) NOT NULL,
35 TRIGGER_NAME VARCHAR(200) NOT NULL,
36 TRIGGER_GROUP VARCHAR(200) NOT NULL,
37 JOB_NAME VARCHAR(200) NOT NULL,
38 JOB_GROUP VARCHAR(200) NOT NULL,
39 DESCRIPTION VARCHAR(250) NULL,
40 NEXT_FIRE_TIME BIGINT(13) NULL,
41 PREV_FIRE_TIME BIGINT(13) NULL,
42 PRIORITY INTEGER NULL,
43 TRIGGER_STATE VARCHAR(16) NOT NULL,
44 TRIGGER_TYPE VARCHAR(8) NOT NULL,
45 START_TIME BIGINT(13) NOT NULL,
46 END_TIME BIGINT(13) NULL,
47 CALENDAR_NAME VARCHAR(200) NULL,
48 MISFIRE_INSTR SMALLINT(2) NULL,
49 JOB_DATA BLOB NULL,
50 PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
51 FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
52 REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP))
53 ENGINE=InnoDB;
54 -- 存储已配置的Simple Trigger的信息
55 CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
56 SCHED_NAME VARCHAR(120) NOT NULL,
57 TRIGGER_NAME VARCHAR(200) NOT NULL,
58 TRIGGER_GROUP VARCHAR(200) NOT NULL,
59 REPEAT_COUNT BIGINT(7) NOT NULL,
60 REPEAT_INTERVAL BIGINT(12) NOT NULL,
61 TIMES_TRIGGERED BIGINT(10) NOT NULL,
62 PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
63 FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
64 REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
65 ENGINE=InnoDB;
66 -- 存储Cron Trigger,包括Cron表达式和时区信息
67 CREATE TABLE QRTZ_CRON_TRIGGERS (
68 SCHED_NAME VARCHAR(120) NOT NULL,
69 TRIGGER_NAME VARCHAR(200) NOT NULL,
70 TRIGGER_GROUP VARCHAR(200) NOT NULL,
71 CRON_EXPRESSION VARCHAR(120) NOT NULL,
72 TIME_ZONE_ID VARCHAR(80),
73 PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
74 FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
75 REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
76 ENGINE=InnoDB;
77 CREATE TABLE QRTZ_SIMPROP_TRIGGERS
78 (
79 SCHED_NAME VARCHAR(120) NOT NULL,
80 TRIGGER_NAME VARCHAR(200) NOT NULL,
81 TRIGGER_GROUP VARCHAR(200) NOT NULL,
82 STR_PROP_1 VARCHAR(512) NULL,
83 STR_PROP_2 VARCHAR(512) NULL,
84 STR_PROP_3 VARCHAR(512) NULL,
85 INT_PROP_1 INT NULL,
86 INT_PROP_2 INT NULL,
87 LONG_PROP_1 BIGINT NULL,
88 LONG_PROP_2 BIGINT NULL,
89 DEC_PROP_1 NUMERIC(13,4) NULL,
90 DEC_PROP_2 NUMERIC(13,4) NULL,
91 BOOL_PROP_1 VARCHAR(1) NULL,
92 BOOL_PROP_2 VARCHAR(1) NULL,
93 PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
94 FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
95 REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
96 ENGINE=InnoDB;
97 -- Trigger作为Blob类型存储(用于Quartz用户用JDBC创建他们自己定制的Trigger类型,JobStore并不知道如何存储实例的时候)
98 CREATE TABLE QRTZ_BLOB_TRIGGERS (
99 SCHED_NAME VARCHAR(120) NOT NULL,
100 TRIGGER_NAME VARCHAR(200) NOT NULL,
101 TRIGGER_GROUP VARCHAR(200) NOT NULL,
102 BLOB_DATA BLOB NULL,
103 PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
104 INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP),
105 FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
106 REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP))
107 ENGINE=InnoDB;
108 -- 以Blob类型存储Quartz的Calendar日历信息,quartz可配置一个日历来指定一个时间范围
109 CREATE TABLE QRTZ_CALENDARS (
110 SCHED_NAME VARCHAR(120) NOT NULL,
111 CALENDAR_NAME VARCHAR(200) NOT NULL,
112 CALENDAR BLOB NOT NULL,
113 PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
114 ENGINE=InnoDB;
115 -- 存储已暂停的Trigger组的信息
116 CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
117 SCHED_NAME VARCHAR(120) NOT NULL,
118 TRIGGER_GROUP VARCHAR(200) NOT NULL,
119 PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
120 ENGINE=InnoDB;
121 -- 存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息
122 CREATE TABLE QRTZ_FIRED_TRIGGERS (
123 SCHED_NAME VARCHAR(120) NOT NULL,
124 ENTRY_ID VARCHAR(95) NOT NULL,
125 TRIGGER_NAME VARCHAR(200) NOT NULL,
126 TRIGGER_GROUP VARCHAR(200) NOT NULL,
127 INSTANCE_NAME VARCHAR(200) NOT NULL,
128 FIRED_TIME BIGINT(13) NOT NULL,
129 SCHED_TIME BIGINT(13) NOT NULL,
130 PRIORITY INTEGER NOT NULL,
131 STATE VARCHAR(16) NOT NULL,
132 JOB_NAME VARCHAR(200) NULL,
133 JOB_GROUP VARCHAR(200) NULL,
134 IS_NONCONCURRENT VARCHAR(1) NULL,
135 REQUESTS_RECOVERY VARCHAR(1) NULL,
136 PRIMARY KEY (SCHED_NAME,ENTRY_ID))
137 ENGINE=InnoDB;
138 -- 存储少量的有关 Scheduler的状态信息,和别的 Scheduler 实例(假如是用于一个集群中)
139 CREATE TABLE QRTZ_SCHEDULER_STATE (
140 SCHED_NAME VARCHAR(120) NOT NULL,
141 INSTANCE_NAME VARCHAR(200) NOT NULL,
142 LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
143 CHECKIN_INTERVAL BIGINT(13) NOT NULL,
144 PRIMARY KEY (SCHED_NAME,INSTANCE_NAME))
145 ENGINE=InnoDB;
146 -- 存储程序的非观锁的信息(假如使用了悲观锁)
147 CREATE TABLE QRTZ_LOCKS (
148 SCHED_NAME VARCHAR(120) NOT NULL,
149 LOCK_NAME VARCHAR(40) NOT NULL,
150 PRIMARY KEY (SCHED_NAME,LOCK_NAME))
151 ENGINE=InnoDB;
152 CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
153 CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);
154 CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
155 CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
156 CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
157 CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
158 CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
159 CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
160 CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
161 CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
162 CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
163 CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
164 CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
165 CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
166 CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
167 CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
168 CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
169 CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
170 CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
171 CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
172 commit;
3.新建完Quartz系统表之后,你需要做SpringBoot和Quartz的相关配置
application.yml
1 quartz:
2 enabled: true
3 server:
4 port: 9090
5 spring:
6 datasource:
7 url: jdbc:mysql://localhost:3306/spring_quartz
8 username: yourname
9 password: yourpassword
10 tomcat:
11 initialSize: 20
12 maxActive: 100
13 maxIdle: 100
14 minIdle: 20
15 maxWait: 10000
16 testWhileIdle: true
17 testOnBorrow: false
18 testOnReturn: false
quartz.properties文件, 注 : 这里如果使用yml文件格式的配置,里面的配置会失效.
1 #ID设置为自动获取 每一个必须不同 (所有调度器实例中是唯一的)
2 org.quartz.scheduler.instanceId=AUTO
3 #指定调度程序的主线程是否应该是守护线程
4 org.quartz.scheduler.makeSchedulerThreadDaemon=true
5 #ThreadPool实现的类名
6 org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
7 #ThreadPool配置线程守护进程
8 org.quartz.threadPool.makeThreadsDaemons=true
9 #线程数量
10 org.quartz.threadPool.threadCount:20
11 #线程优先级
12 org.quartz.threadPool.threadPriority:5
13 #数据保存方式为持久化
14 org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
15 #StdJDBCDelegate说明支持集群
16 org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
17 #quartz内部表的前缀
18 org.quartz.jobStore.tablePrefix=QRTZ_
19 #是否加入集群
20 org.quartz.jobStore.isClustered=true
21 #容许的最大作业延长时间
22 org.quartz.jobStore.misfireThreshold=25000
4.需要调度数据库中的Job实例定义。
JobEntity.class
package com.ealen.entity;
import javax.persistence.*;
import java.io.Serializable;
/**
* Created by EalenXie on 2018/6/4 14:09
* 这里个人示例,可自定义相关属性
*/
@Entity
@Table(name = "JOB_ENTITY")
public class JobEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name; //job名称
private String group; //job组名
private String cron; //执行的cron
private String parameter; //job的参数
private String description; //job描述信息
@Column(name = "vm_param")
private String vmParam; //vm参数
@Column(name = "jar_path")
private String jarPath; //job的jar路径,在这里我选择的是定时执行一些可执行的jar包
private String status; //job的执行状态,这里我设置为OPEN/CLOSE且只有该值为OPEN才会执行该Job
public JobEntity() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getCron() {
return cron;
}
public void setCron(String cron) {
this.cron = cron;
}
public String getParameter() {
return parameter;
}
public void setParameter(String parameter) {
this.parameter = parameter;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getVmParam() {
return vmParam;
}
public void setVmParam(String vmParam) {
this.vmParam = vmParam;
}
public String getJarPath() {
return jarPath;
}
public void setJarPath(String jarPath) {
this.jarPath = jarPath;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public String toString() {
return "JobEntity{" +
"id=" + id +
", name='" + name + '\'' +
", group='" + group + '\'' +
", cron='" + cron + '\'' +
", parameter='" + parameter + '\'' +
", description='" + description + '\'' +
", vmParam='" + vmParam + '\'' +
", jarPath='" + jarPath + '\'' +
", status='" + status + '\'' +
'}';
}
//新增Builder模式,可选,选择设置任意属性初始化对象
public JobEntity(Builder builder) {
id = builder.id;
name = builder.name;
group = builder.group;
cron = builder.cron;
parameter = builder.parameter;
description = builder.description;
vmParam = builder.vmParam;
jarPath = builder.jarPath;
status = builder.status;
}
public static class Builder {
private Integer id;
private String name = ""; //job名称
private String group = ""; //job组名
private String cron = ""; //执行的cron
private String parameter = ""; //job的参数
private String description = ""; //job描述信息
private String vmParam = ""; //vm参数
private String jarPath = ""; //job的jar路径
private String status = ""; //job的执行状态,只有该值为OPEN才会执行该Job
public Builder withId(Integer i) {
id = i;
return this;
}
public Builder withName(String n) {
name = n;
return this;
}
public Builder withGroup(String g) {
group = g;
return this;
}
public Builder withCron(String c) {
cron = c;
return this;
}
public Builder withParameter(String p) {
parameter = p;
return this;
}
public Builder withDescription(String d) {
description = d;
return this;
}
public Builder withVMParameter(String vm) {
vmParam = vm;
return this;
}
public Builder withJarPath(String jar) {
jarPath = jar;
return this;
}
public Builder withStatus(String s) {
status = s;
return this;
}
public JobEntity newJobEntity() {
return new JobEntity(this);
}
}
}
为了方便测试,设计好表之后先插入几条记录,job_entity.sql,相关sql语句如下:
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `job_entity`;
CREATE TABLE `job_entity` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`group` varchar(255) DEFAULT NULL,
`cron` varchar(255) DEFAULT NULL,
`parameter` varchar(255) NOT NULL,
`description` varchar(255) DEFAULT NULL,
`vm_param` varchar(255) DEFAULT NULL,
`jar_path` varchar(255) DEFAULT NULL,
`status` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
INSERT INTO `job_entity` VALUES ('1', 'first', 'helloworld', '0/2 * * * * ? ', '1', '第一个', '', null, 'OPEN');
INSERT INTO `job_entity` VALUES ('2', 'second', 'helloworld', '0/5 * * * * ? ', '2', '第二个', null, null, 'OPEN');
INSERT INTO `job_entity` VALUES ('3', 'third', 'helloworld', '0/15 * * * * ? ', '3', '第三个', null, null, 'OPEN');
INSERT INTO `job_entity` VALUES ('4', 'four', 'helloworld', '0 0/1 * * * ? *', '4', '第四个', null, null, 'CLOSE');
INSERT INTO `job_entity` VALUES ('5', 'OLAY Job', 'Nomal', '0 0/2 * * * ?', '5', '第五个', null, 'C:\\EalenXie\\Download\\JDE-Order-1.0-SNAPSHOT.jar', 'CLOSE');
Quartz的核心配置类 : ConfigureQuartz.class
package com.ealen.config;
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
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 org.springframework.scheduling.quartz.SpringBeanJobFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;
/**
* Created by EalenXie on 2018/6/4 11:02
* Quartz的核心配置类
*/
@Configuration
public class ConfigureQuartz {
//配置JobFactory
@Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
/**
* SchedulerFactoryBean这个类的真正作用提供了对org.quartz.Scheduler的创建与配置,并且会管理它的生命周期与Spring同步。
* org.quartz.Scheduler: 调度器。所有的调度都是由它控制。
* @param dataSource 为SchedulerFactory配置数据源
* @param jobFactory 为SchedulerFactory配置JobFactory
*/
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource, JobFactory jobFactory) throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
//可选,QuartzScheduler启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录
factory.setOverwriteExistingJobs(true);
factory.setAutoStartup(true); //设置自行启动
factory.setDataSource(dataSource);
factory.setJobFactory(jobFactory);
factory.setQuartzProperties(quartzProperties());
return factory;
}
//从quartz.properties文件中读取Quartz配置属性
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
//配置JobFactory,为quartz作业添加自动连接支持
public final 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;
}
}
}
写一个dao访问数据库,JobEntityRepository.class
package com.ealen.dao;
import com.ealen.entity.JobEntity;
import org.springframework.data.repository.CrudRepository;
/**
* Created by EalenXie on 2018/6/4 14:27
*/
public interface JobEntityRepository extends CrudRepository<JobEntity, Long> {
JobEntity getById(Integer id);
}
5.定义调度中心执行逻辑的Job,这个Job的作用就是按照数据库中的Job的逻辑,规则去执行数据库中的Job。
这里使用了一个自定义的枚举工具类StringUtils , StringUtils.class :
package com.ealen.util;
import java.util.List;
import java.util.Map;
/**
* Created by EalenXie on 2018/6/4 14:20
* 自定义枚举单例对象 StringUtil
*/
public enum StringUtils {
getStringUtil;
//是否为空
public boolean isEmpty(String str) {
return (str == null) || (str.length() == 0) || (str.equals(""));
}
//去空格
public String trim(String str) {
return str == null ? null : str.trim();
}
//获取Map参数值
public String getMapString(Map<String, String> map) {
String result = "";
for (Map.Entry entry : map.entrySet()) {
result += entry.getValue() + " ";
}
return result;
}
//获取List参数值
public String getListString(List<String> list) {
String result = "";
for (String s : list) {
result += s + " ";
}
return result;
}
}
调度中心执行逻辑的Job。DynamicJob.class :
1 package com.ealen.job;
2 import com.ealen.util.StringUtils;
3 import org.quartz.*;
4 import org.slf4j.Logger;
5 import org.slf4j.LoggerFactory;
6 import org.springframework.stereotype.Component;
7 import java.io.*;
8 import java.util.ArrayList;
9 import java.util.List;
10 /**
11 * Created by EalenXie on 2018/6/4 14:29
12 * :@DisallowConcurrentExecution : 此标记用在实现Job的类上面,意思是不允许并发执行.
13 * :注意org.quartz.threadPool.threadCount线程池中线程的数量至少要多个,否则@DisallowConcurrentExecution不生效
14 * :假如Job的设置时间间隔为3秒,但Job执行时间是5秒,设置@DisallowConcurrentExecution以后程序会等任务执行完毕以后再去执行,否则会在3秒时再启用新的线程执行
15 */
16 @DisallowConcurrentExecution
17 @Component
18 public class DynamicJob implements Job {
19 private Logger logger = LoggerFactory.getLogger(DynamicJob.class);
20 /**
21 * 核心方法,Quartz Job真正的执行逻辑.
22 * @param executorContext executorContext JobExecutionContext中封装有Quartz运行所需要的所有信息
23 * @throws JobExecutionException execute()方法只允许抛出JobExecutionException异常
24 */
25 @Override
26 public void execute(JobExecutionContext executorContext) throws JobExecutionException {
27 //JobDetail中的JobDataMap是共用的,从getMergedJobDataMap获取的JobDataMap是全新的对象
28 JobDataMap map = executorContext.getMergedJobDataMap();
29 String jarPath = map.getString("jarPath");
30 String parameter = map.getString("parameter");
31 String vmParam = map.getString("vmParam");
32 logger.info("Running Job name : {} ", map.getString("name"));
33 logger.info("Running Job description : " + map.getString("JobDescription"));
34 logger.info("Running Job group: {} ", map.getString("group"));
35 logger.info("Running Job cron : " + map.getString("cronExpression"));
36 logger.info("Running Job jar path : {} ", jarPath);
37 logger.info("Running Job parameter : {} ", parameter);
38 logger.info("Running Job vmParam : {} ", vmParam);
39 long startTime = System.currentTimeMillis();
40 if (!StringUtils.getStringUtil.isEmpty(jarPath)) {
41 File jar = new File(jarPath);
42 if (jar.exists()) {
43 ProcessBuilder processBuilder = new ProcessBuilder();
44 processBuilder.directory(jar.getParentFile());
45 List<String> commands = new ArrayList<>();
46 commands.add("java");
47 if (!StringUtils.getStringUtil.isEmpty(vmParam)) commands.add(vmParam);
48 commands.add("-jar");
49 commands.add(jarPath);
50 if (!StringUtils.getStringUtil.isEmpty(parameter)) commands.add(parameter);
51 processBuilder.command(commands);
52 logger.info("Running Job details as follows >>>>>>>>>>>>>>>>>>>>: ");
53 logger.info("Running Job commands : {} ", StringUtils.getStringUtil.getListString(commands));
54 try {
55 Process process = processBuilder.start();
56 logProcess(process.getInputStream(), process.getErrorStream());
57 } catch (IOException e) {
58 throw new JobExecutionException(e);
59 }
60 } else throw new JobExecutionException("Job Jar not found >> " + jarPath);
61 }
62 long endTime = System.currentTimeMillis();
63 logger.info(">>>>>>>>>>>>> Running Job has been completed , cost time : " + (endTime - startTime) + "ms\n");
64 }
65 //打印Job执行内容的日志
66 private void logProcess(InputStream inputStream, InputStream errorStream) throws IOException {
67 String inputLine;
68 String errorLine;
69 BufferedReader inputReader = new BufferedReader(new InputStreamReader(inputStream));
70 BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream));
71 while ((inputLine = inputReader.readLine()) != null) logger.info(inputLine);
72 while ((errorLine = errorReader.readLine()) != null) logger.error(errorLine);
73 }
74 }
6.为了方便控制Job的运行,为调度中心添加相关的业务逻辑 :
DynamicJobService.class :
1 package com.ealen.service;
2 import com.ealen.dao.JobEntityRepository;
3 import com.ealen.entity.JobEntity;
4 import com.ealen.job.DynamicJob;
5 import org.quartz.*;
6 import org.springframework.beans.factory.annotation.Autowired;
7 import org.springframework.stereotype.Service;
8 import java.util.ArrayList;
9 import java.util.List;
10 /**
11 * Created by EalenXie on 2018/6/4 14:25
12 */
13 @Service
14 public class DynamicJobService {
15 @Autowired
16 private JobEntityRepository repository;
17 //通过Id获取Job
18 public JobEntity getJobEntityById(Integer id) {
19 return repository.getById(id);
20 }
21 //从数据库中加载获取到所有Job
22 public List<JobEntity> loadJobs() {
23 List<JobEntity> list = new ArrayList<>();
24 repository.findAll().forEach(list::add);
25 return list;
26 }
27 //获取JobDataMap.(Job参数对象)
28 public JobDataMap getJobDataMap(JobEntity job) {
29 JobDataMap map = new JobDataMap();
30 map.put("name", job.getName());
31 map.put("group", job.getGroup());
32 map.put("cronExpression", job.getCron());
33 map.put("parameter", job.getParameter());
34 map.put("JobDescription", job.getDescription());
35 map.put("vmParam", job.getVmParam());
36 map.put("jarPath", job.getJarPath());
37 map.put("status", job.getStatus());
38 return map;
39 }
40 //获取JobDetail,JobDetail是任务的定义,而Job是任务的执行逻辑,JobDetail里会引用一个Job Class来定义
41 public JobDetail geJobDetail(JobKey jobKey, String description, JobDataMap map) {
42 return JobBuilder.newJob(DynamicJob.class)
43 .withIdentity(jobKey)
44 .withDescription(description)
45 .setJobData(map)
46 .storeDurably()
47 .build();
48 }
49 //获取Trigger (Job的触发器,执行规则)
50 public Trigger getTrigger(JobEntity job) {
51 return TriggerBuilder.newTrigger()
52 .withIdentity(job.getName(), job.getGroup())
53 .withSchedule(CronScheduleBuilder.cronSchedule(job.getCron()))
54 .build();
55 }
56 //获取JobKey,包含Name和Group
57 public JobKey getJobKey(JobEntity job) {
58 return JobKey.jobKey(job.getName(), job.getGroup());
59 }
60 }
JobController.class :
package com.ealen.web;
import com.ealen.entity.JobEntity;
import com.ealen.service.DynamicJobService;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.Set;
/**
* Created by EalenXie on 2018/6/4 16:12
*/
@RestController
public class JobController {
private static Logger logger = LoggerFactory.getLogger(JobController.class);
@Autowired
private SchedulerFactoryBean factory;
@Autowired
private DynamicJobService jobService;
//初始化启动所有的Job
@PostConstruct
public void initialize() {
try {
reStartAllJobs();
logger.info("INIT SUCCESS");
} catch (SchedulerException e) {
logger.info("INIT EXCEPTION : " + e.getMessage());
e.printStackTrace();
}
}
//根据ID重启某个Job
@RequestMapping("/refresh/{id}")
public String refresh(@PathVariable Integer id) throws SchedulerException {
String result;
JobEntity entity = jobService.getJobEntityById(id);
if (entity == null) return "error: id is not exist ";
TriggerKey triggerKey = new TriggerKey(entity.getName(), entity.getGroup());
JobKey jobKey = jobService.getJobKey(entity);
Scheduler scheduler = factory.getScheduler();
try {
scheduler.unscheduleJob(triggerKey);
scheduler.deleteJob(jobKey);
JobDataMap map = jobService.getJobDataMap(entity);
JobDetail jobDetail = jobService.geJobDetail(jobKey, entity.getDescription(), map);
if (entity.getStatus().equals("OPEN")) {
scheduler.scheduleJob(jobDetail, jobService.getTrigger(entity));
result = "Refresh Job : " + entity.getName() + "\t jarPath: " + entity.getJarPath() + " success !";
} else {
result = "Refresh Job : " + entity.getName() + "\t jarPath: " + entity.getJarPath() + " failed ! , " +
"Because the Job status is " + entity.getStatus();
}
} catch (SchedulerException e) {
result = "Error while Refresh " + e.getMessage();
}
return result;
}
//重启数据库中所有的Job
@RequestMapping("/refresh/all")
public String refreshAll() {
String result;
try {
reStartAllJobs();
result = "SUCCESS";
} catch (SchedulerException e) {
result = "EXCEPTION : " + e.getMessage();
}
return "refresh all jobs : " + result;
}
/**
* 重新启动所有的job
*/
private void reStartAllJobs() throws SchedulerException {
Scheduler scheduler = factory.getScheduler();
Set<JobKey> set = scheduler.getJobKeys(GroupMatcher.anyGroup());
for (JobKey jobKey : set) {
scheduler.deleteJob(jobKey);
}
for (JobEntity job : jobService.loadJobs()) {
logger.info("Job register name : {} , group : {} , cron : {}", job.getName(), job.getGroup(), job.getCron());
JobDataMap map = jobService.getJobDataMap(job);
JobKey jobKey = jobService.getJobKey(job);
JobDetail jobDetail = jobService.geJobDetail(jobKey, job.getDescription(), map);
if (job.getStatus().equals("OPEN")) scheduler.scheduleJob(jobDetail, jobService.getTrigger(job));
else logger.info("Job jump name : {} , Because {} status is {}", job.getName(), job.getName(), job.getStatus());
}
}
}
7.现在已经完事具备,只欠东风了,写一个启动类,跑一下看看效果。
1 package com.ealen;
2 import org.springframework.boot.SpringApplication;
3 import org.springframework.boot.autoconfigure.SpringBootApplication;
4 /**
5 * Created by EalenXie on 2018/6/4 11:00
6 */
7 @SpringBootApplication
8 public class QuartzApplication {
9 public static void main(String[] args) {
10 SpringApplication.run(QuartzApplication.class, args);
11 }
12 }
运行效果如下 :
看到这里,那么恭喜你,Quartz集群已经搭建成功了。如果部署该项目应用到多个服务器上面,Job会在多个服务器上面执行,但同一个Job只会在某个服务器上面执行,即如果服务器A在某个时间执行了某个Job,则其他服务器如B,C,D在此时间均不会执行此Job。即不会造成该Job被多次执行。
这里可以看到数据库中的Job已经在Quartz注册并初始化成功了。
这里可看到Scheduler已经在工作了,Job也已经按照cron在定时执行了。
执行一个包含jar的Job,看一下效果,图下已经看到JOB在正常运行了 :
看到Job执行完成了。
此时如果在数据库中手动修改某个Job的执行cron,并不会马上生效,则可以调用上面写到的业务方法,/refresh/all,则可刷新所有的Job,或/refresh/{id},刷新某个Job。
SpringBoot2.0.3整合Quartz2.3.0实现定时任务的更多相关文章
- Spring定时任务,Spring4整合quartz2.2,quartz-scheduler定时任务
Spring4整合quartz2.2,quartz-scheduler定时任务,Spring定时任务 >>>>>>>>>>>>& ...
- springboot2.1.7整合mybati3.5.2与mysql8.0.13
springboot2.x已经发布一段时间,博主在这里使用springboot2.1.7整合mybatis3.5.2,使用的数据库为mysql8.0.13 1. 导入依赖 <!--mysql-- ...
- Spring4 + Quartz-2.2.0集成实例
Spring3.0不支持Quartz2.0,因为org.quartz.CronTrigger在2.0从class变成了一个interface造成IncompatibleClassChangeError ...
- spring3.2.8+quartz2.2.0(比较全,对比quartz1.x的配置)
spring3.2.8 + quartz2.2.0报错: java.lang.IncompatibleClassChangeError: class org.springframework.sched ...
- solr8.0 springboot整合solr(四)
引言: solr搭建起后,就该应用到java后台开发里了,接下来就用springboot整合应用solr 一:引入jar包 <!--solr--> <dependency> & ...
- Springboot 2.0.4 整合Mybatis出现异常Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
在使用Springboot 2.0.4 整合Mybatis的时候出现异常Property 'sqlSessionFactory' or 'sqlSessionTemplate' are require ...
- security和oauth2.0的整合
security和oauth2.0的整合 之前已经介绍过security的相关的介绍,现在所需要做的就是security和oauth2.0的整合,在原有的基础上我们加上一些相关的代码;代码实现如下: ...
- 基于WPF系统框架设计(5)-Ribbon整合Avalondock 2.0实现多文档界面设计(二)
AvalonDock 是一个.NET库,用于在停靠模式布局(docking)中排列一系列WPF/WinForm控件.最新发布的版本原生支持MVVM框架.Aero Snap特效并具有更好的性能. Ava ...
- spring boot 2.x 系列 —— spring boot 整合 servlet 3.0
文章目录 一.说明 1.1 项目结构说明 1.2 项目依赖 二.采用spring 注册方式整合 servlet 2.1 新建过滤器.监听器和servlet 2.2 注册过滤器.监听器和servlet ...
随机推荐
- 【Java基础专题】编码与乱码(05)---GBK与UTF-8之间的转换
原文出自:http://www.blogjava.net/pengpenglin/archive/2010/02/22/313669.html 在很多论坛.网上经常有网友问" 为什么我使用 ...
- 如何判断一个字符串是否是UTF8编码
UTF8是以8bits即1Bytes为编码的最基本单位,当然也可以有基于16bits和32bits的形式,分别称为UTF16和UTF32,但目前用得不多,而UTF8则被广泛应用在文件储存和网络传输中. ...
- c++的单例模式及c++11对单例模式的优化
单例模式 单例模式,可以说设计模式中最常应用的一种模式了,据说也是面试官最喜欢的题目.但是如果没有学过设计模式的人,可能不会想到要去应用单例模式,面对单例模式适用的情况,可能会优先考虑使用全局或者静态 ...
- Verilog 语言 001 --- 入门级 --- 编写一个半加器电路模块
Verilog 语言编写一个 半加器 电路模块 半加器 的电路结构: S = A 异或 B C = A 与 B 1. 程序代码 module h_adder (A, B, SO, CO); input ...
- GCD 学习(四) dispatch_group
如果想在dispatch_queue中所有的任务执行完成后在做某种操作,在串行队列中,可以把该操作放到最后一个任务执行完成后继续,但是在并行队列中怎么做呢.这就有dispatch_group 成组操作 ...
- GCD 学习(一)简介
文章摘抄至:http://zhuyanfeng.com/archives/3015 并有一些改动 GCD(Grand Central Dispatch)是从OS X Snow Leopard和iOS ...
- CH24C 逃不掉的路
edcc缩点之后跳倍增lca 丢个edcc缩点模板 Code: #include <cstdio> #include <cstring> using namespace std ...
- 《Maven实战》笔记-4-生命周期和插件
除了坐标.依赖以及仓库外,Maven另外两个核心概念是生命周期和插件. 一.生命周期 Maven的生命周期是抽象的,其本身不做任务实际的工作,实际的任务(如编译源代码)都交由插件来完成. 三套生命周期 ...
- C#与数据库访问技术总结(三)之 Connection对象的常用方法
说明:前面(一)(二)总结了数据库连接的概念以及连接数据库的字符串中的各个参数的含义.这篇随笔介绍connection对象的常用方法. Connection对象的常用方法 Connection类型的对 ...
- C/C++使用心得:enum与int的相互转换
如何正确理解enum类型? 例如: enum Color { red, white, blue}; Color x; 我们应说x是Color类型的,而不应将x理解成enumeration类型,更不应将 ...