哥哥教你不引入第三方Quartz的定时任务

环境:
    SpringBoot 2.X
    JDK 8
    Maven 3.5.X


OK,开始正文. . .

因为目标是实现基本的定时任务,另外ToB且为单体,所以可以忽略并发,那么这种场景的话引入第三方Quartz框架(如XXL-Job)有些多余,所以我们从框架本身出发,看是否有合适的包供我们使用(需要做调研),那么结果是好的,我们可以借助org.springframework.scheduling来实现我们的需求。
在这里插入图片描述

看到上边的图片我想大家有了一些想法,是的没错,我们可以通过注解方式@Scheduled来实现,也可以通过实现SchedulingConfigurer接口来实现。

首先来实现第一种,没有任何难度,如下:

@Component
@Configuration      //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling   // 2.开启定时任务
public class ScheduleTask {
    //3.添加定时任务
    @Scheduled(cron = "0/1 * * * * ?")
    //或直接指定时间间隔,例如:1秒
    //@Scheduled(fixedRate=1000)
    private void configureTasks() {
        System.err.println("执行静态定时任务时间: " + LocalDateTime.now());
    }
}

我说的没错吧,一点难度没有,但是也有需要注意的小Tips。着重看一下@Scheduled属性值,心疼你们我把源码扒下来说一下各部分吧。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) // 这是第一个需要注意的点,这个注解只加在方法上或者注解之上生效
@Retention(RetentionPolicy.RUNTIME) // 并且是运行时
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {

	/**
	 * 定时禁用标志。
	 */
	String CRON_DISABLED = ScheduledTaskRegistrar.CRON_DISABLED;


	/**
	 * 定义定时执行时间,默认值"",表示该值无效。
	 */
	String cron() default "";

	/**
	 * 定义时区,默认值""。
	 */
	String zone() default "";

	/**
	 * 定义任务下次开始执行的间隔时间,从上一次任务执行完成开始计算,单位毫秒。默认值-1L,表示该值设置无效。
	 */
	long fixedDelay() default -1;

	/**
	 * 定义任务下次开始执行的间隔时间,从上一次任务执行完成开始计算,单位毫秒。与fixedDelay不同只在于值的格式。默认值"",表示该值设置无效。
	 */
	String fixedDelayString() default "";

	/**
	 * 定义每两次任务的间隔频率,从上一次任务开始执行开始计算,单位毫秒。默认值-1L,表示该值无效。
	 */
	long fixedRate() default -1;

	/**
	 * 定义每两次任务的间隔频率,从上一次任务开始执行开始计算,单位毫秒。与fixedRate不同只在于值的格式。默认值"",表示该值无效。
	 */
	String fixedRateString() default "";

	/**
	 * 定义第一次执行的延迟执行时间,单位秒。默认值-1L,表示没有延迟。
	 */
	long initialDelay() default -1;

	/**
	 * 定义第一次执行的延迟执行时间,与initialDelayDelay()不同在于这里使用表达式,而不是以秒为单位。默认值"",表示没有延迟。
	 */
	String initialDelayString() default "";
}

其中有一个属性是我们大多数(少数不知道)下要传的,那就是cron,这个属性直接关系到这个任务什么时候执行。cron是一个字符串表达式。
cronExpression定义时间规则,Cron表达式由6或7个空格分隔的时间字段组成:秒 分钟 小时 日期 月份 星期 年(可选)

字段允许值允许的特殊字符
0-59, - * /
0-59, - * /
小时0-23, - * /
日期1-31, - * ? / L W C
月份1-12, - * /
星期1-7, - * ? / L C #
1970-2099, - * /

http://blog.csdn.net/supingemail/article/details/22274279
表达式生成网址:https://cron.qqe2.com/

注解实现方式讲的差不多了,那么接下来就说一下基于SchedulingConfigurer接口的实现方式

先来一个❓,为什么有了注解这种简单的方式还要有接口方式实现呢?答案很简单,因为注解方式满足不了所有的需求。他的执行频率(或者说任务的执行周期)是固定的(有Cron表达式决定)

接口方式实现如下:

@Slf4j
@Component
@Configuration // 1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling // 2.开启定时任务
public class MessageScheduleTask implements SchedulingConfigurer {
    private final TrainingService trainingService;

    public MessageScheduleTask(TrainingService trainingService) {
        this.trainingService = trainingService;
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.addTriggerTask(
                () -> System.out.println("执行动态定时任务: " + LocalDateTime.now().toLocalTime()),
                triggerContext -> {
	                // 业务代码
                    Workitem workitem =
                            trainingService.getWorkitem(
                                    new QueryWrapper<Workitem>()
                                            .eq("id", "60bdee1ed7a2b72a6f7c1916"));
                    String date = workitem.getDate();
                    String start = workitem.getStart();
                    String end = workitem.getEnd();
                    String endDateTime = date + " " + end;
                    String cron = null;
                    try {
                        cron = CronUtil.getCron(endDateTime);
                    } catch (ParseException e) {
                        log.info("时间格式转换失败,请手动补偿 " + workitem.getId());
                        e.printStackTrace();
                    }
                    //                    String cron = "0/5 * * * * ?";
                    return new CronTrigger(Objects.requireNonNull(cron)).nextExecutionTime(triggerContext);
                });
    }
}
/** @author sunshaocong */
public class CronUtil {

    private static final ThreadLocal<DateFormat> DATE_FORMAT =
            ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm"));
    private static final ThreadLocal<DateFormat> FORMAT =
            ThreadLocal.withInitial(() -> new SimpleDateFormat("ss mm HH dd MM ?"));

    /**
     * 日期转cron表达式格式
     *
     * @param date 日期
     * @return 日期表达式格式
     */
    public static synchronized String formatDateByPattern(String date) throws ParseException {
        Date realDate = DATE_FORMAT.get().parse(date);
        return FORMAT.get().format(realDate);
    }

    /**
     * 获取cron表达式
     *
     * @param date 日期
     * @return cron表达式
     */
    public static String getCron(String date) throws ParseException {
        return formatDateByPattern(date);
    }
}

到这里讲一下,其实我本来不想用SimpleDateFormatter来将日期转换成Cron表达式(最开始想用DateTimeFormatter来实现),但是没实现成功。所以就用SimpleDateTimeFormatter来吧(这个类是线程不安全的,所以在使用时一定要注意,要么像我那样用ThreadLocal用共享对象,要么就用sychronized来锁定范围)

最后,在使用时还有可能会有问题(如果你项目中有websocket)的话。问题和解决方案如下:
https://www.cnblogs.com/threadj/articles/10631193.html

最后的最后,本篇文章只是浅显的实现,具体的内部调用可以看如下文章:
https://my.oschina.net/u/3773302/blog/4704918

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

new_repo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值