用线程池 + 触发器实现动态去创建定时器的功能,仅做参考,有需要的可根据自身的业务需求做相关的调整
(有帮助的话希望支持下)
先贴个流程图:
思路说明:
1、ThreadPoolTaskScheduler:线程池任务调度类,能够开启线程池进行任务调度。
2、ThreadPoolTaskScheduler.schedule():创建一个定时计划ScheduledFuture,在这个方法需要添加两个参数,Runnable(线程接口类) 和CronTrigger(定时任务触发器)
3、在ScheduledFuture中有一个cancel可以停止定时任务。
案例说明:
本例用到6个类:
1、QuatzDynamicScheduled:业务逻辑入口
2、ScheduledRunnable:线程池
3、QuatzScheduled:业务需求,指定时间执行的定时器
4、ScheduledFutureMap:记录创建的定时器
5、MeetingScheduled:业务实体封装
6、MeetingUserScheduled:业务实体封装
注意:相关操作数据库的不贴出来。
所需Jar包: Quartz相关依赖包
源码:
/**
* Description
* 动态创建定时器
* ======================
* ======================
*
* @Author created by lgy
* @Date 2019/12/19
*/
@Component
public class QuatzDynamicScheduled implements CommandLineRunner {
private static final Logger log = LoggerFactory.getLogger(QuatzDynamicScheduled.class);
@Autowired
private ThreadPoolTaskScheduler poolTaskScheduler;
@Autowired
private ITDynamicTimerService dynamicTimerService;
@Autowired
private IMeetingService meetingService;
@Autowired
private IMeetingUserService meetingUserService;
@Autowired
private ITUserAttendeeService userAttendeeService;
private ScheduledFuture<?> future;
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
return new ThreadPoolTaskScheduler();
}
/**
* 关闭触发器.
*
* @param name
* @return
*/
public void closeDynamicScheduled(String name) {
future = ScheduledFutureMap.getFutures().get(name);
if (future != null) {
future.cancel(true);
ScheduledFutureMap.getFutures().remove(name);
}
}
/**
* 添加触发器.
*
* @param meetingScheduled
* @param type
* @param cron
*/
public void addScheduled(MeetingScheduled meetingScheduled, Integer type, String cron) {
String name = meetingScheduled.getName();
/**
* 判断如果触发器已经存在,先关闭再创建新的触发器
* 预防情况:
* 1、修改与会人员触发.
* 2、修改通知时间触发.
*/
if (ScheduledFutureMap.getFutures().get(name) != null) {
closeDynamicScheduled(name);
}
ScheduledRunnable scheduledRunnable = new ScheduledRunnable(meetingScheduled, type, name);
CronTrigger trigger = new CronTrigger(cron);
future = this.poolTaskScheduler.schedule(scheduledRunnable, trigger);
ScheduledFutureMap.getFutures().put(name, future);
}
/**
* 添加动态定时器.
*
* @param meetingScheduled
* @param type 0:发送短信 1:发送邮件 2:发送短信和邮件
* @param cron cron规则
* cron表达式中各时间元素使用空格进行分割,表达式有至少6个(也可能7个)分别表示如下含义:
*
* 秒(0~59)
* 分钟(0~59)
* 小时(0~23)
* 天(月)(0~31,但是你需要考虑你月的天数)
* 月(0~11)
* 天(星期)(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT)
* 年份(1970-2099)
* 其中每个元素可以是一个值(如6),一个连续区间(9-12),一个间隔时间(8-18/4)(/表示每隔4小时),一个列表(1,3,5),通配符。由于"月份中的日期"和"星期中的日期"这两个元素互斥的,必须要对其中一个设置?.
*
* 0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
* 0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
* 0 0 12 ? * WED 表示每个星期三中午12点
* "0 0 12 * * ?" 每天中午12点触发
* "0 15 10 ? * *" 每天上午10:15触发
* "0 15 10 * * ?" 每天上午10:15触发
* "0 15 10 * * ? *" 每天上午10:15触发
* "0 15 10 * * ? 2005" 2005年的每天上午10:15触发
* "0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
* "0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
* "0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
* "0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
* "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
* "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
* "0 15 10 15 * ?" 每月15日上午10:15触发
* "0 15 10 L * ?" 每月最后一日的上午10:15触发
* "0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
* "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
* "0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
* @return
*/
public boolean addScheduledToSendMsg(MeetingScheduled meetingScheduled, Integer type, String cron) {
log.info("动态添加定时器,参数列表:{'type':{},'cron':{},'meetingScheduled':{}}", new Object[]{type, cron, meetingScheduled});
if (logicNotNull(meetingScheduled)) {
Date notiFyTime = meetingScheduled.getNotifyTime();
addOrUpdateRecordToDataBase(notiFyTime, meetingScheduled.getName());
/*判断 只有为当天的通知时间才创建触发器,否则只添加到数据库*/
if (isToday(notiFyTime.getTime())) {
addScheduled(meetingScheduled, type, cron);
}
return true;
}
return false;
}
/**
* 添加动态定时器.
*
* @param meetingScheduled
* @param type
* @param notifyTime
* @return
*/
public boolean addScheduledToSendMsg(MeetingScheduled meetingScheduled, Integer type, Date notifyTime) {
log.info("动态添加定时器,参数列表:{'type':{},'notifyTime':{},'meetingScheduled':{}}", new Object[]{type, notifyTime, meetingScheduled});
Calendar calendar = Calendar.getInstance();
calendar.setTime(notifyTime);
int year = calendar.get(Calendar.YEAR),
month = calendar.get(Calendar.MONTH),
day = calendar.get(Calendar.DATE),
hour = calendar.get(Calendar.HOUR_OF_DAY),
min = calendar.get(Calendar.MINUTE),
sec = calendar.get(Calendar.SECOND);
String cron = sec + " " + min + " " + hour + " " + day + " " + month + " ? " + year;
addScheduledToSendMsg(meetingScheduled, type, cron);
return false;
}
/**
* 添加动态定时器
*
* @param meeting
* @param userAttendees
* @param type
* @param cron
* @return
*/
public boolean addScheduledToSendMsg(Meeting meeting, List<UserAttendee> userAttendees, Integer type, String cron) {
if (userAttendees != null && userAttendees.size() > 0) {
List<MeetingUserScheduled> users = new ArrayList<>();
for (UserAttendee userAttendee : userAttendees) {
MeetingUserScheduled user = new MeetingUserScheduled();
user.setEmail(userAttendee.getEmail());
user.setName(userAttendee.getName());
user.setPhone(userAttendee.getPhone());
// 校验用户的非空参数,不满足 直接返回
if (!logicNotNull(user)) {
return false;
}
users.add(user);
}
MeetingScheduled meetingScheduled = new MeetingScheduled();
meetingScheduled.setEndTime(meeting.getEndTime());
meetingScheduled.setMeetingContent(meeting.getDescription());
meetingScheduled.setMeetingTitle(meeting.getTitle());
meetingScheduled.setRoomName(meeting.getRoomIdName());
meetingScheduled.setStartTime(meeting.getStartTime());
meetingScheduled.setNotifyTime(meeting.getNotificationTime());
meetingScheduled.setName(meeting.getName());
meetingScheduled.setUsers(users);
return addScheduledToSendMsg(meetingScheduled, type, cron);
}
return false;
}
/**
* 添加动态定时器
*
* @param meeting
* @param userAttendees
* @param type
* @param notifyTime
* @return
*/
public boolean addScheduledToSendMsg(Meeting meeting, List<UserAttendee> userAttendees, Integer type, Date notifyTime) {
if (userAttendees != null && userAttendees.size() > 0) {
List<MeetingUserScheduled> users = new ArrayList<>();
for (UserAttendee userAttendee : userAttendees) {
MeetingUserScheduled user = new MeetingUserScheduled();
user.setEmail(userAttendee.getEmail());
user.setName(userAttendee.getName());
user.setPhone(userAttendee.getPhone());
// 校验用户的非空参数,不满足 直接返回
if (!logicNotNull(user)) {
return false;
}
users.add(user);
}
MeetingScheduled meetingScheduled = new MeetingScheduled();
meetingScheduled.setEndTime(meeting.getEndTime());
meetingScheduled.setMeetingContent(meeting.getDescription());
meetingScheduled.setMeetingTitle(meeting.getTitle());
meetingScheduled.setRoomName(meeting.getRoomIdName());
meetingScheduled.setStartTime(meeting.getStartTime());
meetingScheduled.setNotifyTime(meeting.getNotificationTime());
meetingScheduled.setName(meeting.getName());
meetingScheduled.setUsers(users);
Calendar calendar = Calendar.getInstance();
calendar.setTime(notifyTime);
int year = calendar.get(Calendar.YEAR),
month = calendar.get(Calendar.MONTH),
day = calendar.get(Calendar.DATE),
hour = calendar.get(Calendar.HOUR_OF_DAY),
min = calendar.get(Calendar.MINUTE),
sec = calendar.get(Calendar.SECOND);
String cron = sec + " " + min + " " + hour + " " + day + " " + month + " ? " + year;
return addScheduledToSendMsg(meetingScheduled, type, cron);
}
return false;
}
/**
* 检验会议通知的内容相关字段非空
*
* @param meetingScheduled
* @return
*/
private boolean logicNotNull(MeetingScheduled meetingScheduled) {
boolean b1 = meetingScheduled.getEndTime() != null,
b3 = logicNotNull(meetingScheduled.getMeetingTitle()),
b4 = logicNotNull(meetingScheduled.getRoomName()),
b5 = meetingScheduled.getStartTime() != null,
b6 = meetingScheduled.getUsers() != null && meetingScheduled.getUsers().size() > 0,
b7 = meetingScheduled.getNotifyTime() != null,
b8 = logicNotNull(meetingScheduled.getName());
if (b1 && b3 && b4 && b5 && b6 && b7 && b8) {
return true;
}
return false;
}
/**
* 校验会议通知用户成员相关字段不为空
*
* @param meetingUserScheduled
* @return
*/
private boolean logicNotNull(MeetingUserScheduled meetingUserScheduled) {
boolean b1 = meetingUserScheduled.getEmail() != null && !meetingUserScheduled.equals(""),
b2 = meetingUserScheduled.getName() != null && !meetingUserScheduled.equals(""),
b3 = meetingUserScheduled.getPhone() != null && !meetingUserScheduled.equals("");
if (b1 && b2 && b3) {
return true;
}
return false;
}
/**
* 字符串判断非空
*
* @param param
* @return
*/
private boolean logicNotNull(String param) {
if (param != null && !param.equals("")) {
return true;
}
return false;
}
/**
* 添加 or 更新
* 定时器记录到数据库
*
* @param notifyTime
*/
private void addOrUpdateRecordToDataBase(Date notifyTime, String name) {
try {
TDynamicTimer dynamicTimer = new TDynamicTimer();
dynamicTimer.setCreateTime(new Date());
dynamicTimer.setDisable(1);
dynamicTimer.setName(name);
dynamicTimer.setNotifyTime(notifyTime);
Map<String, Object> params = new HashMap<>();
params.put("name", name);
List<TDynamicTimer> dynamicTimers = this.dynamicTimerService.selectByMap(params);
if (dynamicTimers != null && dynamicTimers.size() > 0) {
dynamicTimer.setId(dynamicTimers.get(0).getId());
this.dynamicTimerService.updateById(dynamicTimer);
} else {
this.dynamicTimerService.insert(dynamicTimer);
}
} catch (Exception e) {
log.error(e.getMessage());
}
}
/**
* 获取当天所有未执行的定时器
*
* @return
*/
private List<TDynamicTimer> findDynamicTimers() {
return this.dynamicTimerService.findAllTodayDynamicTimers();
}
/**
* 根据name获取会议信息.
*
* @param name
* @return
*/
private Meeting getMeetingsByName(String name) {
Meeting meeting = this.meetingService.getMeetingListByName(name);
return meeting;
}
/**
* 根据meetingId获取相关与会人员id
*
* @param meetingId
* @return
*/
private List<Long> findMeetingUsersByMeetingId(Long meetingId) {
Map<String, Object> params = new HashMap<>();
params.put("meeting_id", meetingId);
List<MeetingUser> meetingUsers = this.meetingUserService.selectByMap(params);
List<Long> userIds = new ArrayList<>();
if (meetingUsers != null && meetingUsers.size() > 0) {
for (MeetingUser meetingUser : meetingUsers) {
userIds.add(meetingUser.getUserId());
}
}
return userIds;
}
/**
* 根据与会人员id获取用户详情信息.
*
* @param userIds
* @return
*/
private List<UserAttendee> findMeetingUsersInfoByUserIds(List<Long> userIds) {
List<UserAttendee> userAttendees = this.userAttendeeService.selectBatchIds(userIds);
return userAttendees;
}
/**
* 判断是否是今天
*
* @param time
* @return
*/
private boolean isToday(long time) {
return isThisTime(time, "yyyy-MM-dd");
}
private boolean isThisTime(long time, String pattern) {
Date date = new Date(time);
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
String param = sdf.format(date);//参数时间
String now = sdf.format(new Date());//当前时间
if (param.equals(now)) {
return true;
}
return false;
}
/**
* 检测未执行的定时器,并创建当天的定时器
*/
public void checkUnExecutedTask() {
List<TDynamicTimer> dynamicTimers = findDynamicTimers();
if (dynamicTimers != null && dynamicTimers.size() > 0) {
for (TDynamicTimer dynamicTimer : dynamicTimers) {
/*会议表 && 触发器记录表 关联的业务流水号*/
String name = dynamicTimer.getName();
/**
* 1、根据流水号:name 获取相关的 meeting
* 2、根据meetingId 获取相关的meetingUsers
* 3、调用创建动态定时器方法
*/
Meeting meeting = getMeetingsByName(name);
if (meeting != null) {
List<Long> userIds = findMeetingUsersByMeetingId(meeting.getMeetingId());
if (userIds != null && userIds.size() > 0) {
List<UserAttendee> userAttendees = findMeetingUsersInfoByUserIds(userIds);
addScheduledToSendMsg(meeting, userAttendees, meeting.getNotifyType(), meeting.getNotificationTime());
}
}
}
}
}
/**
* 应用启动成功后执行该方法
*
* @param args
* @throws Exception
*/
@Override
public void run(String... args) throws Exception {
log.info("应用启动成功,自动检测数据库记录,添加尚未处理的定时器");
checkUnExecutedTask();
}
}
/**
* Description
* 线程池
* ======================
* ======================
*
* @Author created by lgy
* @Date 2019/12/19
*/
@Component
public class ScheduledRunnable implements Runnable {
private static final Logger log = LoggerFactory.getLogger(ScheduledRunnable.class);
private MeetingScheduled meetingScheduled;
private Integer type;
private String name;
@Autowired
private ITDynamicTimerService dynamicTimerService;
public ScheduledRunnable() {
}
public ScheduledRunnable(MeetingScheduled meetingScheduled, Integer type, String name) {
this.meetingScheduled = meetingScheduled;
this.type = type;
this.name = name;
}
/*执行完关闭定时器*/
private void closeTimer(String name) {
log.info("关闭执行完的定时器");
ScheduledFuture<?> future = ScheduledFutureMap.getFutures().get(name);
future.cancel(true);
/*修改数据库记录*/
dynamicTimerService.closeDynamicTimers(name);
/*移除Map集合的值*/
ScheduledFutureMap.getFutures().remove(name);
}
@Override
public void run() {
switch (type.intValue()) {
case 0:
toSendMsg(this.meetingScheduled);
closeTimer(name);
break;
case 1:
toSendEmail(this.meetingScheduled);
closeTimer(name);
break;
case 2:
toSendEmailAndMsg(this.meetingScheduled);
closeTimer(name);
break;
default:
log.error("请注意type的值:\n0:发送短信 \n1:发送邮件 \n 2:同时发送短信和邮件");
break;
}
}
/*发送短信*/
public void toSendMsg(MeetingScheduled meetingScheduled) {
log.info("发送短信,参数列表:{'meetingScheduled':{}}", new Object[]{meetingScheduled});
}
/*发送邮件*/
public void toSendEmail(MeetingScheduled meetingScheduled) {
log.info("发送邮件,参数列表:{'meetingScheduled':{}}", new Object[]{meetingScheduled});
}
/*同时发送短信和邮件*/
public void toSendEmailAndMsg(MeetingScheduled meetingScheduled) {
/*发送短信*/
toSendMsg(meetingScheduled);
/*发送邮件*/
toSendEmail(meetingScheduled);
}
public MeetingScheduled getMeetingScheduled() {
return meetingScheduled;
}
public void setMeetingScheduled(MeetingScheduled meetingScheduled) {
this.meetingScheduled = meetingScheduled;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* Description
* 每天执行的定时器
* ======================
* ======================
*
* @Author created by lgy
* @Date 2019/12/23
*/
@Component
public class QuatzScheduled {
@Autowired
private QuatzDynamicScheduled quatzDynamicScheduled;
/*每天凌晨0点01分执行*/
@Scheduled(cron = "0 01 00 ? * *")
public void everyDay() {
this.quatzDynamicScheduled.checkUnExecutedTask();
}
}
/**
* Description
* 定时器集合
* ======================
* ======================
*
* @Author created by jy
* @Date 2019/12/23
*/
public class ScheduledFutureMap {
private static final Map<String, ScheduledFuture<?>> FUTURES = new HashMap<>();
public static Map<String, ScheduledFuture<?>> getFutures() {
return FUTURES;
}
}
/**
* Description
*
* ======================
* ======================
*
* @Author created by jy
* @Date 2019/12/19
*/
public class MeetingUserScheduled implements Serializable {
private String name;
private String phone;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
/**
* Description
*
* ======================
* ======================
*
* @Author created by jy
* @Date 2019/12/19
*/
public class MeetingScheduled {
/*会议标题*/
private String meetingTitle;
/*会议内容*/
private String meetingContent;
/*会议开始时间*/
private Date startTime;
/*会议结束时间*/
private Date endTime;
/*开会会议室名称*/
private String roomName;
/*通知开会时间*/
private Date notifyTime;
// 业务流水号
private String name;
/*通知的与会人员*/
private List<MeetingUserScheduled> users;
public String getMeetingTitle() {
return meetingTitle;
}
public void setMeetingTitle(String meetingTitle) {
this.meetingTitle = meetingTitle;
}
public String getMeetingContent() {
return meetingContent;
}
public void setMeetingContent(String meetingContent) {
this.meetingContent = meetingContent;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public String getRoomName() {
return roomName;
}
public void setRoomName(String roomName) {
this.roomName = roomName;
}
public List<MeetingUserScheduled> getUsers() {
return users;
}
public void setUsers(List<MeetingUserScheduled> users) {
this.users = users;
}
public Date getNotifyTime() {
return notifyTime;
}
public void setNotifyTime(Date notifyTime) {
this.notifyTime = notifyTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}