参考了网上的一些文档,发现写都都很细碎,且没有提供一个完成实例的源码,特此写了一个实例并上传到了github上供大家参考
项目地址: https://github.com/frankliu121/springboot-curd
springboot 版本为2.x之后的则在spring启动器中已经包含了quart的依赖知己依赖启动器即可;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
要是1.5.9则要使用以下添加依赖:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
需注意quartz 1.x与quartz 2.x建表语句有所区别,可以去quartz官网去下载,我这里使用的是2.x版本的建表脚本:
#
# 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;
QRTZ_CALENDARS 以 Blob 类型存储 Quartz 的 Calendar 信息 QRTZ_CRON_TRIGGERS 存储CronTrigger触发器信息,包括Cron表达式和时区等信息
QRTZ_FIRED_TRIGGERS 存储已触发的Trigger状态信息和关联的Job执行信息
QRTZ_PAUSED_TRIGGER_GRPS 存储已暂停的Trigger组信息
QRTZ_SCHEDULER_STATE 存储有关Scheduler的状态信息
QRTZ_LOCKS 存储程序锁信息
QRTZ_JOB_DETAILS 存储Job的详细信息
QRTZ_JOB_LISTENERS 存储Job配置的JobListener信息
QRTZ_SIMPLE_TRIGGERS 存储SimpleTrigger触发器信息,包括重复次数,间隔等信息
QRTZ_BLOG_TRIGGERS 存储Blob类型的Trigger,一般用于自定义触发器
QRTZ_TRIGGER_LISTENERS 存储已配置的TriggerListener信息
QRTZ_TRIGGERS 存储已配置的Trigger的信息
spring:
quartz:
properties:
org:
quartz:
scheduler:
instanceName: clusteredScheduler
instanceId: AUTO
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
isClustered: false #是否使用集群
clusterCheckinInterval: 10000
useProperties: false
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 10
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
#数据库方式
job-store-type: jdbc
#初始化表结构
#jdbc:
#initialize-schema: never
package lf.ssm.config;
import org.quartz.*;
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
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 java.io.IOException;
/**
* @Classname QuartzConfig
* @Date 2020/4/10 17:23
* @Created by liufeng
*/
@Configuration
public class QuartzConfig {
//配置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;
}
}
}
储存任务信息可以根据自己的业务需求再扩展字段
package lf.ssm.entity.job;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lf.ssm.core.base.BaseModel;
import lf.ssm.util.ValidType;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @Classname JobEntity
* @Date 2020/4/10 17:01
* @Created by liufeng
*/
@Data
@Accessors(chain = true)
@ApiModel("任务实体")
@TableName("JOB_ENTITY")
public class JobEntity extends BaseModel {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.ID_WORKER)
@ApiModelProperty(value="id",name="id")
@NotNull( message = "id不能为空",groups = {ValidType.Update.class,ValidType.Delete.class})
private Long id;
@ApiModelProperty(value="job名称",name="name")
@NotNull( message = "任务名不能为空",groups = {ValidType.Add.class,ValidType.Update.class})
@NotBlank( message = "任务名不能为空",groups = {ValidType.Add.class,ValidType.Update.class})
private String name;
@ApiModelProperty(value="job分组",name="job_group")
private String jobGroup;
@ApiModelProperty(value="执行的表达式",name="cronExpression")
@TableField("cron_expression")
@NotNull( message = "表达式不能为空",groups = {ValidType.Add.class,ValidType.Update.class})
@NotBlank( message = "表达式不能为空",groups = {ValidType.Add.class,ValidType.Update.class})
private String cronExpression;
@ApiModelProperty(value="job的参数",name="parameter")
private String parameter;
@ApiModelProperty(value="job描述信息",name="description")
private String description;
@ApiModelProperty(value="标识job的执行状态 0:已停用 1已启用",name="status")
@NotNull( message = "状态不能为空",groups = {ValidType.Add.class,ValidType.Update.class})
@NotBlank( message = "状态不能为空",groups = {ValidType.Add.class,ValidType.Update.class})
private String status;
@ApiModelProperty(value="触发器名字",name="triggerName")
@TableField("trigger_name")
private String triggerName;
@ApiModelProperty(value="触发器分组",name="triggerGroup")
@TableField("trigger_group")
private String triggerGroup;
@ApiModelProperty(value="执行类名(包名+类名)",name="jobClassName")
@NotNull( message = "类名不能为空",groups = {ValidType.Add.class,ValidType.Update.class})
@NotBlank( message = "类名不能为空",groups = {ValidType.Add.class,ValidType.Update.class})
@TableField("job_class_name")
private String jobClassName;
@ApiModelProperty(value="执行的方法名",name="jobClassName")
@NotNull( message = "方法名不能为空",groups = {ValidType.Add.class,ValidType.Update.class})
@NotBlank( message = "方法名不能为空",groups = {ValidType.Add.class,ValidType.Update.class})
@TableField("method_name")
private String methodName;
@ApiModelProperty(value="是否立即运行,0:否,1:是",name="isNowRun")
@TableField("is_now_run")
private String isNowRun;
@ApiModelProperty(value="备注",name="remark")
private String remark;
@ApiModelProperty(value="表达式解析后的值显示",name="cronExpressionVal")
@TableField(exist = false)
private String cronExpressionVal;
}
package lf.ssm.util;
import lf.ssm.entity.job.JobEntity;
import org.quartz.CronExpression;
import org.quartz.JobKey;
import org.quartz.TriggerKey;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
/**
* @Classname CronExpParserUtil
* @Date 2020/4/11 16:33
* @Created by liufeng
*/
public class JobUtil {
public final static String DEFAULT_GROUP= "DEFAULT_GROUP";
public final static String TRIGGER_PREFIX= "TRIGGER_";
/**
* 生成JobKey
* @param job
* @return
*/
public static JobKey genJobKey(JobEntity job) {
return new JobKey(job.getName().trim(), DEFAULT_GROUP);
}
/**
* 产生TriggerKey
*
* @param job
* @return
*/
public static TriggerKey genTriggerKey(JobEntity job) {
return new TriggerKey(TRIGGER_PREFIX + job.getName().trim(), DEFAULT_GROUP);
}
/**
* 解析corn表达式,生成指定日期的时间序列
*
* @param cronExpression cron表达式
* @param cronDate cron解析日期
* @param result crom解析时间序列
* @return 解析成功失败
*/
public static boolean parser(String cronExpression, String cronDate, List<String> result) {
if (cronExpression == null || cronExpression.length() < 1 || cronDate == null || cronDate.length() < 1) {
return false;
} else {
CronExpression exp = null;
// 初始化cron表达式解析器
try {
exp = new CronExpression(cronExpression);
} catch (ParseException e) {
e.printStackTrace();
}
// 定义生成时间范围
// 定义开始时间,前一天的23点59分59秒
Calendar c = Calendar.getInstance();
String sStart = cronDate + " 00:00:00";
SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date dStart = null;
try {
dStart = sdf.parse(sStart);
} catch (ParseException e) {
e.printStackTrace();
}
c.setTime(dStart);
c.add(Calendar.SECOND, -1);
dStart = c.getTime();
// 定义结束时间,当天的23点59分59秒
c.add(Calendar.DATE, 1);
Date dEnd = c.getTime();
// 生成时间序列
java.util.Date dd = dStart;
dd = exp.getNextValidTimeAfter(dd);
while ((dd.getTime() >= dStart.getTime()) && (dd.getTime() <= dEnd.getTime())) {
result.add(sdf.format(dd));
dd = exp.getNextValidTimeAfter(dd);
}
exp = null;
}
return true;
}
public static String translateToChinese(String cronExp) {
if (cronExp == null || cronExp.length() < 1) {
return "cron表达式为空";
}
CronExpression exp = null;
// 初始化cron表达式解析器
try {
exp = new CronExpression(cronExp);
} catch (ParseException e) {
return "corn表达式不正确";
}
String[] tmpCorns = cronExp.split(" ");
StringBuffer sBuffer = new StringBuffer();
if (tmpCorns.length == 6) {
//解析月
if (!tmpCorns[4].equals("*")) {
sBuffer.append(tmpCorns[4]).append("月");
} else {
sBuffer.append("每月");
}
//解析周
if (!tmpCorns[5].equals("*") && !tmpCorns[5].equals("?")) {
char[] tmpArray = tmpCorns[5].toCharArray();
for (char tmp : tmpArray) {
switch (tmp) {
case '1':
sBuffer.append("星期天");
break;
case '2':
sBuffer.append("星期一");
break;
case '3':
sBuffer.append("星期二");
break;
case '4':
sBuffer.append("星期三");
break;
case '5':
sBuffer.append("星期四");
break;
case '6':
sBuffer.append("星期五");
break;
case '7':
sBuffer.append("星期六");
break;
case '-':
sBuffer.append("至");
break;
default:
sBuffer.append(tmp);
break;
}
}
}
//解析日
if (!tmpCorns[3].equals("?")) {
if (!tmpCorns[3].equals("*")) {
sBuffer.append(tmpCorns[3]).append("日");
} else {
sBuffer.append("每日");
}
}
//解析时
if (!tmpCorns[2].equals("*")) {
sBuffer.append(tmpCorns[2]).append("时");
} else {
sBuffer.append("每时");
}
//解析分
if (!tmpCorns[1].equals("*")) {
sBuffer.append(tmpCorns[1]).append("分");
} else {
sBuffer.append("每分");
}
//解析秒
if (!tmpCorns[0].equals("*")) {
sBuffer.append(tmpCorns[0]).append("秒");
} else {
sBuffer.append("每秒");
}
}
return sBuffer.toString();
}
//测试方法
public static void main(String[] args) {
String CRON_EXPRESSION = "0 0 3 * * ?";
// 生成指定日期的CRON时间序列
String CRON_DATE = "2016-04-26";
System.out.println(CRON_EXPRESSION);
System.out.println(JobUtil.translateToChinese(CRON_EXPRESSION));
List<String> lTime = new ArrayList<String>();
if (!JobUtil.parser(CRON_EXPRESSION, CRON_DATE, lTime)) {
System.out.println("无法生成Cron表达式:日期," + CRON_DATE + ";不符合规则cron表达式:" + CRON_EXPRESSION);
}
for (int i = 0; i < lTime.size(); i++) {
System.out.println(lTime.get(i));
}
}
}
package lf.ssm.service.job;
import lf.ssm.core.base.IBaseService;
import lf.ssm.entity.job.JobEntity;
import lf.ssm.exception.JobException;
/**
*
* 服务类
*
*
* @author liufeng
* @since 2020-04-10
*/
public interface IJobEntityService extends IBaseService<JobEntity> {
/**
* 保存任务
* @param record
*/
void saveTask(JobEntity record);
/**
* 立即执行(触发一次任务)
* @param record
* @throws JobException
*/
void trigger(JobEntity record);
/**
* 暂停任务
* @param record
*/
void pause(JobEntity record);
/**
* 恢复任务
* @param record
* @throws JobException
*/
void resume(JobEntity record);
/**
* 移除任务
* @param jobEntity
* @throws JobException
*/
void remove(JobEntity jobEntity);
/**
* 更新任务
* @param record
* @throws JobException
*/
void updateTask(JobEntity record);
}
7.JobEntityServiceImpl
6.package lf.ssm.service.impl.job;
import lf.ssm.core.impl.BaseServiceImpl;
import lf.ssm.entity.job.JobEntity;
import lf.ssm.exception.JobException;
import lf.ssm.mapper.job.JobEntityMapper;
import lf.ssm.service.job.IJobEntityService;
import lf.ssm.util.BeanUtil;
import lf.ssm.util.Const;
import lf.ssm.util.JobUtil;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.Optional;
/**
*
* 服务实现类
*
*
* @author liufeng
* @since 2020-04-10
*/
@Service
@Slf4j
public class JobEntityServiceImpl extends BaseServiceImpl<JobEntity,JobEntityMapper> implements IJobEntityService {
@Autowired
private Scheduler scheduler;
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class)
public void saveTask(@RequestBody JobEntity record) {
try {
buildJob(record);
insert(record);
} catch (Exception e) {
throw new JobException(201,"新增任务失败");
}
}
/**
* 立即执行
* quartz是通过临时生成一个trigger的方式来实现的,这个trigger将在本次任务运行完成之后自动删除。
* @param record
* @throws JobException
*/
@Override
public void trigger(JobEntity record) {
try {
scheduler.triggerJob(JobUtil.genJobKey(record));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void pause(JobEntity record) {
try {
record.setStatus(Const.DEFAULT_NO_STR);
updateById(record);
scheduler.pauseJob(JobUtil.genJobKey(record));
} catch (SchedulerException e) {
e.printStackTrace();
throw new JobException(666,"暂停任务失败");
}
}
@Override
public void resume(JobEntity record) {
try {
record.setStatus(Const.DEFAULT_YES_STR);
updateById(record);
scheduler.resumeJob(JobUtil.genJobKey(record));
} catch (Exception e) {
e.printStackTrace();
throw new JobException(999,"恢复任务失败");
}
}
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class)
public void remove(JobEntity jobEntity){
try {
deleteById(jobEntity.getId());
deleteJob(jobEntity);
} catch (Exception e) {
e.printStackTrace();
throw new JobException(520,"移除任务失败");
}
}
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = JobException.class)
public void updateTask(JobEntity record) throws JobException {
// 1.更新自定义任务实体数据 2.删除原任务相关数据 3.重新构建任务
try {
deleteJob(selectById(record.getId()));
buildJob(record);
updateById(record);
} catch (Exception e) {
e.printStackTrace();
throw new JobException(886,"更新任务失败");
}
}
/**
* 构建定时任务
* @param record
* @throws ClassNotFoundException
* @throws SchedulerException
* @throws IllegalAccessException
* @throws InstantiationException
*/
private void buildJob(JobEntity record) throws ClassNotFoundException, SchedulerException, IllegalAccessException, InstantiationException {
Class cls = Class.forName(record.getJobClassName());
cls.newInstance();
// 构建jobdetail丶 触发器丶调度器
JobDetail job = JobBuilder.newJob(cls).withIdentity(JobUtil.genJobKey(record))
.withDescription(record.getRemark()).build();
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(record.getCronExpression());
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(JobUtil.genTriggerKey(record)) .startNow()
.withSchedule(cronScheduleBuilder).build();
// 传递的参数
job.getJobDataMap().put("jobEntity", BeanUtil.beanToMap(record));
// 调度任务
if(!scheduler.isShutdown()) {
scheduler.scheduleJob(job, trigger);
}
// 启动调度器
if (!scheduler.isStarted()) {
scheduler.start();
}
//未启用
if (Const.DEFAULT_NO_STR.equals(record.getStatus())){
scheduler.pauseJob(JobUtil.genJobKey(record));
}
if (Const.DEFAULT_YES_STR.equals(record.getIsNowRun())) { //是否立即执行一次
trigger(record);
}
}
/**
* 移除任务
* @param record
* @throws SchedulerException
*/
private void deleteJob(JobEntity record) throws SchedulerException {
//移除任务
TriggerKey triggerKey = JobUtil.genTriggerKey(record);
// 停止触发器
scheduler.pauseTrigger(triggerKey);
// 移除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务
scheduler.deleteJob(JobUtil.genJobKey(record));
}
}
实现Job接口,通过JobExecutionContext对象可以获取到我们存的自定义任务实体JobEntity,来获取储存的className以及methodName,通过反射执行其子类实例的方法
package lf.ssm.core.base;
import com.baomidou.mybatisplus.core.toolkit.BeanUtils;
import lf.ssm.entity.job.JobEntity;
import lf.ssm.exception.JobException;
import lf.ssm.util.*;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
/**
* @Classname BaseJob
* @Date 2020/4/11 21:14
* @Created by liufeng
*/
@Slf4j
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class BaseJob implements Job,Serializable {
/**
* 利用反射执行指定的方法
* @param context
* @throws JobExecutionException
*/
@Override
public void execute(JobExecutionContext context) {
JobDataMap JobDataMap = context.getMergedJobDataMap();
Map jobMap = (Map<String,Object>)JobDataMap.get("jobEntity");
if (CollectionUtils.isEmpty(jobMap)){
log.error("获取jobEntity失败");
return;
}
JobEntity jobEntity = null;
Class<?> clazz =null;
try {
jobEntity = BeanUtil.mapToBean(jobMap, JobEntity.class);
clazz = Class.forName(jobEntity.getJobClassName());
String className = StringUtils.toLowerCaseFirstOne(clazz.getSimpleName());
Object bean = (Object) SpringUtil.getBean(className);
Method method = ReflectionUtils.findMethod(bean.getClass(), jobEntity.getMethodName());
log.info("=======开始执行定时任务:"+jobEntity.getJobClassName()+"_"+jobEntity.getMethodName()+"=======");
long start = System.currentTimeMillis();
ReflectionUtils.invokeMethod(method, bean);
long end = System.currentTimeMillis();
log.info("=======定时任务执行完毕,耗时:"+(end-start)+"毫秒,任务执行时间:"+DateUtil.formatDate(context.getFireTime())+",下次执行时间:"+ DateUtil.formatDate(context.getNextFireTime()) +"=======");
} catch (Exception e) {
log.error("定时任务执行失败");
e.printStackTrace();
}
}
}
9.JobEntityController
package lf.ssm.controller.job;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lf.ssm.core.base.BaseResult;
import lf.ssm.entity.job.JobEntity;
import lf.ssm.service.job.IJobEntityService;
import lf.ssm.util.JobUtil;
import lf.ssm.util.ValidType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import lf.ssm.core.base.BaseController;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import java.util.Optional;
/**
*
* 任务实体控制器
*
*
* @author liufeng
* @since 2020-04-10
*/
@Api(description = "定时任务控制器",tags = "JobEntityController")
@RestController
@RequestMapping("/jobEntity")
public class JobEntityController extends BaseController {
@Autowired
private IJobEntityService service;
/**
* 跳转页面
* @param page
* @return
*/
@GetMapping("/page/{page}")
@ApiOperation("跳转页面")
public ModelAndView toPage(@PathVariable("page") String page){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("vue/"+page);
return modelAndView;
}
/**
* 查询所有任务
* @author liufeng
* @date 2020/4/11 13:24
* @return java.util.List
*/
@GetMapping("/list")
@ApiOperation("查询所有任务")
public IPage<JobEntity> list( long pageNum, long pageSize){
IPage<JobEntity> page = service.selectPage(pageNum, pageSize, new QueryWrapper<JobEntity>().orderByDesc("status","create_time"));
//处理corn表达式显示
if (!CollectionUtils.isEmpty(page.getRecords())){
page.getRecords().forEach(r->{
r.setCronExpressionVal(JobUtil.translateToChinese(r.getCronExpression()));
});
}
return page;
}
/**
* 添加定时任务
* @author liufeng
* @date 2020/4/11 13:24
* @return lf.ssm.entity.base.BaseResult
*/
@PostMapping("/addJob")
@ApiOperation("添加定时任务")
public BaseResult addJob(@RequestBody @Validated({ValidType.Add.class}) JobEntity job, BindingResult bindingResult){
BaseResult result = BaseResult.successResult();
job.setTriggerName(JobUtil.TRIGGER_PREFIX+ job.getName());
job.setJobGroup(JobUtil.DEFAULT_GROUP);
job.setTriggerGroup(JobUtil.DEFAULT_GROUP);
service.saveTask(job);
return result;
}
/**
* 更新定时任务
* @author liufeng
* @date 2020/4/11 13:24
* @return lf.ssm.entity.base.BaseResult
*/
@PostMapping("/updateJob")
@ApiOperation("更新定时任务")
public BaseResult updateJob(@RequestBody @Validated({ValidType.Update.class}) JobEntity job, BindingResult bindingResult){
BaseResult result = BaseResult.successResult();
service.updateTask((job));
return result;
}
/**
* 移除定时任务
* @author liufeng
* @date 2020/4/11 13:24
* @return lf.ssm.entity.base.BaseResult
*/
@PostMapping("/removeJob/{id}")
@ApiOperation("移除定时任务")
public BaseResult removeJob(@PathVariable("id") Long id){
BaseResult result = BaseResult.successResult();
if (id == null || id == 0){
return result.error(2,"id不能为空");
}
JobEntity jobEntity = service.selectById(id);
if (jobEntity == null){
return result.error(3,"未查询到实体");
}
service.remove(jobEntity);
return result;
}
/**
* 暂停定时任务
* @author liufeng
* @date 2020/4/11 13:24
* @return lf.ssm.entity.base.BaseResult
*/
@PostMapping("/pauseJob/{id}")
@ApiOperation("暂停定时任务")
public BaseResult pauseJob(@PathVariable("id") Long id){
BaseResult result = BaseResult.successResult();
if (id == null || id == 0){
return result.error(2,"id不能为空");
}
JobEntity jobEntity = service.selectById(id);
if (jobEntity == null){
return result.error(3,"未查询到实体");
}
service.pause(jobEntity);
return result;
}
/**
* 恢复定时任务
* @author liufeng
* @date 2020/4/11 13:24
* @return lf.ssm.entity.base.BaseResult
*/
@PostMapping("/resumeJob/{id}")
@ApiOperation("恢复定时任务")
public BaseResult resumeJob(@PathVariable("id") Long id){
BaseResult result = BaseResult.successResult();
if (id == null || id == 0){
return result.error(2,"id不能为空");
}
JobEntity jobEntity = service.selectById(id);
if (jobEntity == null){
return result.error(3,"未查询到实体");
}
service.resume(jobEntity);
return result;
}
/**
* 触发一次任务
* @author liufeng
* @date 2020/4/11 13:24
* @return lf.ssm.entity.base.BaseResult
*/
@PostMapping("/triggerJob/{id}")
@ApiOperation("触发一次任务")
public BaseResult triggerJob(@PathVariable("id") Long id){
BaseResult result = BaseResult.successResult();
if (id == null || id == 0){
return result.error(2,"id不能为空");
}
JobEntity jobEntity = service.selectById(id);
if (jobEntity == null){
return result.error(3,"未查询到实体");
}
service.trigger(jobEntity);
return result;
}
}
测试场景:
实现每隔20s打印某某某我爱你
实现每隔20s打印你最棒
1.新建类继承BaseJob
package lf.ssm.job;
import lf.ssm.core.base.BaseJob;
import lf.ssm.entity.test.User;
import lf.ssm.service.test.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* Test类定时任务
* @Classname TestJob
* @Date 2020/4/11 21:34
* @Created by liufeng
*/
@DisallowConcurrentExecution
@Component
@Slf4j
public class TestJob extends BaseJob {
@Autowired
private IUserService userService;
/**
* 打印我爱你
*/
public void printLoveYou(){
log.info("======================printLoveYou定时任务开始执行===================");
User user = userService.selectList().get(0);
System.out.println("我爱你"+user.getUsername());
iLoveYou();
log.info("======================printLoveYou定时任务执行结束===================");
}
/**
* 打印你最棒
*/
public void printYouAreBest(){
log.info("======================printLoveYou定时任务开始执行===================");
List<User> users = userService.selectList();
users.forEach(u->{
System.out.println("亲爱的"+u.getUsername()+"你最棒");
});
log.info("======================printLoveYou定时任务执行结束===================");
}
/**
* 打印心形
*/
private void iLoveYou(){
for(float y = (float) 1.5;y>-1.5;y -=0.1) {
for(float x= (float) -1.5;x<1.5;x+= 0.05){
float a = x*x+y*y-1;
if((a*a*a-x*x*y*y*y)<=0.0) {
System.out.print("^");
}
else
System.out.print(" ");
}
System.out.println();
}
}
}
2.配置任务:
访问:http://localhost:8080/jobEntity/page/JobManager