Quartz简介:Quartz是一个完全由java编写的开源作业调度框架,为在Java应用程序中进行作业调度提供了简单却强大的机制等等等,官网http://www.quartz-scheduler.org/
这篇文章主要讲的如何实现Quartz与SSM整合以及前端使用vue+ElementUI实现简单的任务管理页面
实现的功能:页面内添加/编辑/删除/暂停/恢复/停止/终止任务,支持页面内编辑cron,calendar,dailyTime以及simple类型的触发器及其参数。
效果如图
本文没什么讲解,如果稍微懂点的,稍微改一改就可以自己来用了,主要是自己记录一下,主要是后端代码,涉及到的都放了出来,vue就简单放了下首页及编辑页的,前端那点简单,主要是接口没问题就可以。
首先maven引入quartz,目前最新版本为2.3.2
org.quartz-scheduler
quartz
2.3.2
org.quartz-scheduler
quartz-jobs
2.3.2
然后是新建quartz的配置文件,新建文件quartz.properties,位于src/main/resources/quartz.properties
# 固定前缀org.quartz
# 主要分为scheduler、threadPool、jobStore、plugin等部分
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
# 实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# threadCount和threadPriority将以setter的形式注入ThreadPool实例
# 并发个数
org.quartz.threadPool.threadCount = 5
# 优先级
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
#持久化 不需要了,在SchedulerConfig里获取了Spring的数据源
#org.quartz.jobStore.misfireThreshold = 5000
#org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
#org.quartz.jobStore.tablePrefix = QRTZ_
#org.quartz.jobStore.dataSource = qzDS
#org.quartz.dataSource.qzDS.driver = com.mysql.cj.jdbc.Driver
#org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/lkAdmin?allowPublicKeyRetrieval=true&useUnicode=true&useSSL=false&allowMultiQueries=true&useAffectedRows=true&characterEncoding=utf8&serverTimezone=UTC
#org.quartz.dataSource.qzDS.user = XX
#org.quartz.dataSource.qzDS.password = XX
#org.quartz.dataSource.qzDS.maxConnections = 10
然后去新建下Quartz需要的数据库表,以下为MySql的语句,如果其他数据库去Quartz官网查询, 下载完整的文档,然后在docs目录下的dbTables文件夹里找到对应的创建表的方法。
#
# Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
#
# PLEASE consider using mysql with innodb tables to avoid locking issues
#
# In your Quartz properties file, you'll need to set
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#
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)
);
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)
);
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)
);
CREATE TABLE QRTZ_CRON_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
CRON_EXPRESSION VARCHAR(200) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
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)
);
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)
);
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),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
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)
);
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);
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)
);
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)
);
CREATE TABLE QRTZ_LOCKS
(
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);
commit;
接下来请再去新建一个视图v_schedule,便于之后mybatis的mapper所调用
SELECT
`qrtz_job_details`.`JOB_NAME` AS `JobName`,
`qrtz_job_details`.`JOB_GROUP` AS `JobGroup`,
`qrtz_job_details`.`JOB_CLASS_NAME` AS `JobClassName`,
`qrtz_job_details`.`DESCRIPTION` AS `Description`,
`qrtz_triggers`.`TRIGGER_NAME` AS `TriggerName`,
`qrtz_triggers`.`TRIGGER_GROUP` AS `TriggerGroup`,
`qrtz_triggers`.`TRIGGER_TYPE` AS `TriggerType`,
`qrtz_triggers`.`TRIGGER_STATE` AS `TriggerState`,
`qrtz_triggers`.`PRIORITY` AS `Priority`,
`qrtz_simple_triggers`.`REPEAT_INTERVAL` AS `RepeatInterval`,
`qrtz_simple_triggers`.`TIMES_TRIGGERED` AS `TimesTriggered`,
`qrtz_simple_triggers`.`REPEAT_COUNT` AS `RepeatCount`,
`qrtz_cron_triggers`.`CRON_EXPRESSION` AS `CronExpression`
FROM
(
(
(
`qrtz_job_details`
LEFT JOIN `qrtz_triggers` ON ( ( ( `qrtz_job_details`.`JOB_NAME` = `qrtz_triggers`.`JOB_NAME` ) AND ( `qrtz_job_details`.`JOB_GROUP` = `qrtz_triggers`.`JOB_GROUP` ) ) )
)
LEFT JOIN `qrtz_simple_triggers` ON ( ( ( `qrtz_triggers`.`TRIGGER_NAME` = `qrtz_simple_triggers`.`TRIGGER_NAME` ) AND ( `qrtz_triggers`.`TRIGGER_GROUP` = `qrtz_simple_triggers`.`TRIGGER_GROUP` ) ) )
)
LEFT JOIN `qrtz_cron_triggers` ON ( ( ( `qrtz_triggers`.`TRIGGER_NAME` = `qrtz_cron_triggers`.`TRIGGER_NAME` ) AND ( `qrtz_triggers`.`TRIGGER_GROUP` = `qrtz_cron_triggers`.`TRIGGER_GROUP` ) ) )
)
然后通过@Configuration定义Quartz的一些配置,其中主要是数据源,新建文件SchedulerConfig
@Configuration
public class SchedulerConfig {
/**
* 配置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;
}
}
}
基本配置终于完成了,接下来就可以开始使用他了。首先新建一个BaseJob接口
public interface BaseJob extends Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException;
}
接下来可以新建第一个任务了,新建HelloJob类
@Schedule(name = "hello", description = "该任务会打印hello")
@Slf4j
public class HelloJob implements BaseJob {
public HelloJob() {
}
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
log.error("Hello Job执行时间: " + new Date());
}
}
其中注解@Schedule是我自定义的一个注解,这里也放一下吧,是便于前端配置任务时,可以看到任务的相关信息。
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Schedule {
String name() default "";
String description() default "";
}
然后开始定义Mapper,Service,Entity以及Controller
新建SchedulerMapper.xml
SELECT JobName,JobGroup,JobClassName,TriggerName,TriggerGroup,
RepeatInterval,TimesTriggered,CronExpression,Description,TriggerType,TriggerState,
Priority,RepeatInterval,TimesTriggered,RepeatCount from v_schedule
SELECT JobName,JobGroup,JobClassName,TriggerName,TriggerGroup,
RepeatInterval,TimesTriggered,CronExpression,Description,TriggerType,TriggerState,
Priority,RepeatInterval,TimesTriggered,RepeatCount from v_schedule where JobName=#{jobName}
and JobGroup=#{jobGroup}
新建ScheduleEntity.class
@Data
public class SchedulerEntity {
/**
* 任务名称
*/
private String jobName;
/**
* 任务分组
*/
private String jobGroup;
/**
* 任务描述
*/
private String description;
/**
* 任务所在类
*/
private String jobClassName;
/**
* 触发器名称
*/
private String triggerName;
/**
* 触发器分组
*/
private String triggerGroup;
/**
* 触发器状态
*/
private String triggerState;
/**
* 触发器类型
*/
private String triggerType;
/**
* 优先级
*/
private Integer priority;
/**
* cron表达式
*/
private String cronExpression;
/**
* simple类型触发器属性 (MILLISECOND,SECOND,MINUTE,HOUR)
* 重复间隔
*/
private Long repeatInterval;
/**
* 重复次数
*/
private Integer repeatCount;
/**
* 已触发次数
*/
private Long timesTriggered;
/**
* calendar,dailyTime(SECOND, MINUTE or HOUR)
* 时间间隔
*/
private Integer timeInterval;
/**
* 间隔单位
*/
private String intervalUnit;
}