携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
本人的记性不是很好,比较容易忘记、遗漏一些待办的事项。因为经常遗漏一些事情,时常惹老婆生气。这是我想要做一个待办事项提醒的初衷。本篇文章只讲述了待办事项的核心部分 —— 定时任务。
本篇文章中用到的定时任务只有一种,当满足定时任务的触发时间点时,会向事件表(t_day_event)中插入一条待办事项。但是每个定时任务的触发时间是不同的。
一些待办事项,我这里简单举几个例子:
- 每天都要拖地;
- 每天出门前要检查水桶是否还有水,没水的话要去饮水机打水;
- 每周的星期天要刷马桶、洗马桶垫。
我对待办事项的种类大体划分了一下,大体可以分为如下几种:
- 未来某一天要做的且只做一次的事情;
- 每天都要做的事情;
- 每周周X要做的事情;
- 每月X号要做的事情;
- 每间隔X天要做的事情;
对应这5种待办事项我预定义了5种Cron表达式,详情如下:
@Getter
public enum TaskType implements BaseEnum<String> {
// 每天要做的事情
DAILY("daily", "0 0 0 * * ?"),
// 每周周X要做的事情
WEEKLY("weekly", "0 0 0 ? * {}"),
// 每月X号要做的事情
MONTHLY("monthly", "0 0 0 {} * ?"),
// 每隔X天要做的事情
INTERVAL("interval", "0 0 0 1/{} * ? "),
// 只做一次的事情
ONCE("once", "");
/**
* 类型编码
*/
private String code;
/**
* 表达式模板
*/
private String cronPattern;
TaskType(String code, String cronPattern) {
this.code = code;
this.cronPattern = cronPattern;
}
/**
* 根据类型编码获取任务类型
* @param code 类型编码
* @return 任务类型
*/
public static TaskType getTaskType(String code) {
for (TaskType taskType : TaskType.values()) {
if (Objects.equals(taskType.getCode(), code)) {
return taskType;
}
}
return null;
}
}
未来某一天要做的且只做一次的事情不走定时任务逻辑,不是本篇文章讲述的重点。(意思就是未来某一天要做的且只做一次的事情的逻辑实现,我们这里不讲了。)
我画了一个创建定时任务的流程图,方便大家理解。
添加定时任务具体实现类
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.ScheduledFuture;
@Slf4j
@Service
public class JobService {
@Resource
private TaskScheduler taskScheduler;
private Map<String, ScheduledFuture<?>> jobMap = new HashMap<>();
/**
* 添加定时任务
* @param jobId 任务ID
* @param job 任务
* @param cronExpression cron表达式
*/
public void addTask(String jobId, Runnable job, String cronExpression) {
log.info("任务ID:[{}],任务表达式:[{}]", jobId, cronExpression);
ScheduledFuture<?> scheduledFuture = taskScheduler.schedule(job,
new CronTrigger(cronExpression, TimeZone.getTimeZone(TimeZone.getDefault().getID()))
);
jobMap.put(jobId, scheduledFuture);
log.info("内存中总的任务数:{}", jobMap.keySet().size());
}
}
addTask()方法包含三个参数:
jobId:定时任务的惟一标识,可以通过此标识从Job Map找到该任务;job:我们定义的任务,里面包含我们想要定时执行的具体业务代码;cronExpression:cron表达式,满足cron表达式时会执行job的run()方法。
我们添加一个定时任务的流程大体是:
- 定义一个任务类,实现
Runnable接口; - 明确步骤1中定义的任务类想要触发的cron表达式;
- 调用
JobService的addTask()方法。
经过上面三步操作,当时间点满足cron表达式指定的时间点时就会执行自定义任务类的run()方法。
TaskScheduler,任务调度器接口,默认实现是org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler。
下面是待办事项定时任务的具体代码实现:
@Slf4j
@Service
public class DayEventJob implements Runnable {
private DayEvent dayEvent;
@Resource
private DayEventDao dayEventDao;
public void setDayEvent(DayEvent dayEvent) {
this.dayEvent = dayEvent;
}
@Override
public void run() {
log.info("day event job run...");
if (Objects.nonNull(dayEvent)) {
dayEvent.setId(Id.next());
dayEvent.setCreateTime(new Date());
dayEvent.setEventDate(new Date());
dayEventDao.insert(dayEvent);
} else {
log.info("day event object is null.");
}
}
}
DayEventJob实现了Runnable,作用是保存一条事项数据到数据库。