- pom.xml依赖
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
-
主启动类上加上 @EnableScheduling
-
表结构
-- 定时器相关配置表
CREATE TABLE if not exists QRTZ_JOB_DETAILS(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME VARCHAR(190) NOT NULL,
JOB_GROUP VARCHAR(190) 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 if not exists QRTZ_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
JOB_NAME VARCHAR(190) NOT NULL,
JOB_GROUP VARCHAR(190) 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(190) 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 if not exists QRTZ_SIMPLE_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) 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 if not exists QRTZ_CRON_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) 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 if not exists QRTZ_SIMPROP_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) 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 if not exists QRTZ_BLOB_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) 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 if not exists QRTZ_CALENDARS (
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME VARCHAR(190) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME))
ENGINE=InnoDB;
CREATE TABLE if not exists QRTZ_PAUSED_TRIGGER_GRPS (
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP))
ENGINE=InnoDB;
CREATE TABLE if not exists QRTZ_FIRED_TRIGGERS (
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(190) NOT NULL,
TRIGGER_GROUP VARCHAR(190) NOT NULL,
INSTANCE_NAME VARCHAR(190) 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(190) NULL,
JOB_GROUP VARCHAR(190) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID))
ENGINE=InnoDB;
CREATE TABLE if not exists QRTZ_SCHEDULER_STATE (
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(190) 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 if not exists QRTZ_LOCKS (
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME))
ENGINE=InnoDB;
- 定时任务配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
import java.util.Properties;
/**
* 定时任务配置
*/
@Configuration
public class ScheduleConfig {
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
//quartz参数
Properties prop = new Properties();
prop.put("org.quartz.scheduler.instanceName", "SchedulerName");
prop.put("org.quartz.scheduler.instanceId", "AUTO");
//线程池配置
prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
prop.put("org.quartz.threadPool.threadCount", "20");
prop.put("org.quartz.threadPool.threadPriority", "5");
//JobStore配置
//定时任务抛出 org.quartz.SchedulerConfigException: DataSource name not set. 异常.
//prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore");
//集群配置
prop.put("org.quartz.jobStore.isClustered", "true");
prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
prop.put("org.quartz.jobStore.misfireThreshold", "12000");
prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
//PostgreSQL数据库,需要打开此注释
//prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
factory.setQuartzProperties(prop);
factory.setSchedulerName("SchedulerName");
//延时启动
factory.setStartupDelay(30);
factory.setApplicationContextSchedulerContextKey("applicationContextKey");
//可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
factory.setOverwriteExistingJobs(true);
//设置自动启动,默认为true
factory.setAutoStartup(true);
return factory;
}
}
- 定时任务工具类
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ScheduleJobUtils {
private static final Logger logger = LoggerFactory.getLogger(ScheduleJobUtils.class);
public static void deleteScheduleJob(Scheduler scheduler, String NAME, String JOB_NAME, String JOB_GROUP) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME, JOB_GROUP);
if( scheduler.checkExists(triggerKey) ){
//如果存在测删除
scheduler.pauseTrigger(triggerKey);
scheduler.unscheduleJob(triggerKey);
}
JobKey jobKey = new JobKey(JOB_NAME, JOB_GROUP);
if(scheduler.checkExists(jobKey)){
scheduler.pauseJob(jobKey); //停止任务
scheduler.deleteJob(jobKey); //删除任务
}
logger.info("删除【"+NAME+"】定时任务成功!");
}catch (Exception e) {
logger.error("删除【"+NAME+"】定时任务失败!", e);
}
}
/**
* 方法名:addOrUpdateScheduleJob
*
* @param NAME 任务中文名称
* @param JOB_NAME 任务名
* @param JOB_GROUP 任务组
* @param jobClass 任务执行类
*/
public static void addOrUpdateScheduleJob(Scheduler scheduler, String NAME, String JOB_NAME, String JOB_GROUP, String CronExpression, Class <? extends Job> jobClass){
try{
deleteScheduleJob(scheduler, NAME, JOB_NAME, JOB_GROUP);
//CronExpression-表达式调度构建器
/**
Cron表达式范例:
每隔5秒执行一次:0/5 * * * * ?
每隔1分钟执行一次:0 0/1 * * * ?
每天23点执行一次:0 0 23 * * ?
每天凌晨1点执行一次:0 0 1 * * ?
每月1号凌晨1点执行一次:0 0 1 1 * ?
每月最后一天23点执行一次:0 0 23 L * ?
每周星期天凌晨1点实行一次:0 0 1 ? * L
在26分、29分、33分执行一次:0 26,29,33 * * * ?
每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
*/
CronExpression cronExpressionObj = new CronExpression(CronExpression);
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpressionObj).withMisfireHandlingInstructionDoNothing();
//创建新触发条件
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String datestr = sdf.format(new Date()) + " 00:00:00";
Date startDate = stringToDateTime(datestr);
//Date startDate = new Date(System.currentTimeMillis()+5*1000);
CronTrigger newcorntrigger = TriggerBuilder.newTrigger()
.withIdentity(JOB_NAME, JOB_GROUP)
.startAt(startDate)
.withSchedule(scheduleBuilder)
.build();
//创建任务对象
JobKey jobKey = new JobKey(JOB_NAME, JOB_GROUP);
SimpleDateFormat sdfall = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
JobDetail executeJob= JobBuilder.newJob(jobClass)
.withIdentity(jobKey)
.withDescription(sdfall.format(new Date()))
.storeDurably(true)//没有trigger关联时自动删除
.requestRecovery()//失败后重做
//.usingJobData("createtaskdaynum", createtaskdaynum)
.build();
//按新的trigger重新设置job执行
scheduler.scheduleJob(executeJob, newcorntrigger);
logger.info("启动或更新【"+NAME+"】定时任务成功!");
}catch(Exception e){
logger.error("启动或更新【"+NAME+"】定时任务失败!", e);
}
}
/**
* 手动执行任务
* @param scheduler
* @param JOB_NAME
* @param JOB_GROUP
*/
public static void triggerScheduleJob(Scheduler scheduler, String JOB_NAME, String JOB_GROUP){
try{
JobKey jobKey = new JobKey(JOB_NAME, JOB_GROUP);
scheduler.triggerJob(jobKey);
}catch(Exception e){
logger.error("手动执行【"+JOB_NAME+"】定时任务失败!", e);
}
}
/**
* 方法名称:isMatch
* @param crontabExpress 表达式(如:* * 7 * * ? *)
* @param date
* @return
* @throws java.text.ParseException
* 作用说明:此函数的作用是判断给定的时间 是否 满足 crontab 表达式【忽略毫秒】
*/
private static boolean isMatch(String crontabExpress, Date date) {
try {
if(crontabExpress==null){
return false;
}
if(crontabExpress.equals("")){
return false;
}
CronExpression crontabExpression = new CronExpression(crontabExpress);
return crontabExpression.isSatisfiedBy(date);
} catch (Exception e) {
logger.error("错误的cron表达式"+crontabExpress, e);
return false;
}
}
/**
* 字符转换日期时间对象
*/
private static Timestamp stringToDateTime(String time) {
try{
SimpleDateFormat formatter = null;
if ((time.indexOf("/") > -1) && (time.indexOf(" ") > -1)) {
formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
} else if ((time.indexOf("-") > -1) && (time.indexOf(" ") > -1)) {
formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}else if ((time.indexOf(".") > -1) && (time.indexOf(" ") > -1)) {
formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
} else if ((time.indexOf("/") > -1) && (time.indexOf(" ") == -1)) {
formatter = new SimpleDateFormat("yyyy/MM/dd");
} else if ((time.indexOf("-") > -1) && (time.indexOf(" ") == -1)) {
formatter = new SimpleDateFormat("yyyy-MM-dd");
}else if ((time.indexOf(".") > -1) && (time.indexOf(" ") == -1)) {
formatter = new SimpleDateFormat("yyyy.MM.dd");
}else{
formatter = new SimpleDateFormat("yyyyMMddHHmmss");
}
Date ctime = formatter.parse(time);
Timestamp reval = new Timestamp(ctime.getTime());
return reval;
}catch(Exception e){
return null;
}
}
}
- 日期工具类
import java.sql.Timestamp;
/**
* 日期处理
*/
public class DateUtils {
/**
* timestamp 转 cron表达式
* @param timestamp
* @return
*/
public static String convertToCronExpression(Timestamp timestamp) {
// 从Timestamp中提取小时、分钟和秒
int hour = timestamp.toLocalDateTime().getHour();
int minute = timestamp.toLocalDateTime().getMinute();
int second = timestamp.toLocalDateTime().getSecond();
// 构建每日的cron表达式
String cronExpression = second + " " + minute + " " + hour + " * * ?";
return cronExpression;
}
}
- TestScheduled 类,新增定时任务
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.sql.Timestamp;
@Data
@Slf4j
@Component
public class TestScheduled {
@Autowired
private ITestService testService;
@Resource
private Scheduler scheduler;
@PostConstruct
public void init() {
try{
log.info("系统启动中, 注册定时任务");
// 查数据库获取有效的 数据
List<TestObj> list = new ArrayList<>();
for (TestObj obj : list){
// timestamp 转 cron表达式
Timestamp cron = obj.getTime();
String cronExpression = DateUtils.convertToCronExpression(cron);
// 新增定时任务。obj.getId()参数必须唯一。
ScheduleJobUtils.addOrUpdateScheduleJob(scheduler, "定时执行业务", "test-"+obj.getId(),"system", cronExpression, DetentionJob.class);
}
}catch (Exception e){
log.error("系统启动中, 注册定时任务异常", e);
}
}
}
- Job 任务类, 定时任务到了触发时间执行具体业务逻辑
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.SpringUtil;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
public class TestJob implements Job {
protected Logger logger = LoggerFactory.getLogger(getClass());
private ITestService testService;
public TestJob(){
testService = SpringUtil.getBean(ITestService.class);
}
/**
* 该方法到了触发时间自动触发
*/
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
try{
// 查数据库获取有效的 数据
List<TestEntity> list = new ArrayList<>();
// 处理数据
for (TestEntity obj : list){
// 获取业务时间
Timestamp cron = obj.getTime();
// timestamp 转 cron表达式
String cronExpression = ScheduleJobUtils.convertToCronExpression(cron);
// jobExecutionContext.getFireTime() 为触发时间
boolean match = isMatch(cronExpression, jobExecutionContext.getFireTime());
// 满足业务条件
if (match){
// 执行业务操作
}
}
}catch (Exception e){
logger.error("错误: " , e);
}
}
}