线程池+触发器实现动态创建定时器

用线程池 + 触发器实现动态去创建定时器的功能,仅做参考,有需要的可根据自身的业务需求做相关的调整

(有帮助的话希望支持下)

先贴个流程图:
在这里插入图片描述
思路说明:

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相关依赖包
源码:

/**
 * <p>Description</p>
 * <p>动态创建定时器</p>
 * ======================
 * ======================
 *
 * @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个)分别表示如下含义:
     *                         <p>
     *                         秒(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),通配符。由于"月份中的日期"和"星期中的日期"这两个元素互斥的,必须要对其中一个设置?.
     *                         <p>
     *                         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();
    }

}

/**
 * <p>Description</p>
 * <p>线程池</p>
 * ======================
 * ======================
 *
 * @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;
    }
}
/**
 * <p>Description</p>
 * <p>每天执行的定时器</p>
 * ======================
 * ======================
 *
 * @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();
    }
}
/**
 * <p>Description</p>
 * <p>定时器集合</p>
 * ======================
 * ======================
 *
 * @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;
    }

}
/**
 * <p>Description</p>
 * <p></p>
 * ======================
 * ======================
 *
 * @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;
    }
}

/**
 * <p>Description</p>
 * <p></p>
 * ======================
 * ======================
 *
 * @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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值