JAVA联调实现微信小程序定时发送通知(3)定时任务篇

288 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

了解Java中定时任务的实现

Java中实现定时任务有三种方式:

1.JDK自带的Timer

优点 使用简单方便,属于jdk自带的简单任务调度工具类,如果你的业务逻辑相对简单的话,没必要非得用Quartz等框架,使用Timer是完全可以胜任的。JDK自带的Timer使用的时候会在主线程之外起一个单独的线程执行指定的计划任务,需求不多的话加上线程池可以解决比较简单的实时任务。

缺点也比较明显

  1. Timer不保证任务执行的十分精确, schedule如果某一次调度时间比较长,那么后面的时间会顺延,进而有可能产生一定的时间差

2.不能动态修改时间,修改时间需要整个项目重启,比较麻烦,所以,适用于不怎么需要修改时间的操作

2.Spring中的Spring Task(生产环境中常见)

Spring Task有两种实现方式:

  1. 使用@Scheduled(cron表达式) 直接指定特定的cron表达式,(cron表达式可以指定很多东西,可以看看下面这位大佬写的www.jianshu.com/p/b4b8950fb…)

  2. 基于SchedulingConfigurer注册定时任务

相比之下

  1. @Scheduled不支持动态修改定时周期,只能整个项目重启去修改cron表达式 SchedulingConfigurer支持动态修改,所以优势就很明显

  2. @Scheduled只能是单线程,而SchedulingConfigurer默认是单线程,可以通过添加线程池,实现多线程下定时任务的运行,所以,我们重点学习基于Spring中SchedulingConfigurer实现多定时任务注册及修改执行周期

3.可以借助第三方的Quartz框架

需要去导第三方的依赖包,菜菜的小编还没学到.

下面我们开始看基于Spring中SchedulingConfigurer实现多定时任务注册及修改执行周期的实现

首先先写一个接口,让所有定时任务都实现他,便于定时任务类代码的整洁与规范。


public interface BaskTask extends Runnable {
    //获取执行频率的cron表达式
    String getCron();
    //执行任务逻辑
    void execute();
}

定义一个定时任务类

@Slf4j
@Component
@Data
public class TaskOne implements BaskTask {

    
    private String cron;

   //外部获取执行频率
    @Override
    public String getCron() {
        return cron;
    }
    
    @Override
    public void execute() {
         //可写复杂的任务逻辑
        log.info("taskOne在"+ formatDate(new Date()) +"执行了")
    }
    //执行任务
    @Override
    public void run() {
        execute();
    }
}

最重要的配置部分

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;

import java.util.Date;
import java.util.Map;

/**
 * 定时任务类
 * @author Ethereal
 * Date:2022/9/26 10:34
 */
/*
@EnableScheduling
@Slf4j
@Configuration
public class TaskConfig implements SchedulingConfigurer {
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {

        //获取所有的定时任务
        Map<String, BaskTask> map = applicationContext.getBeansOfType(BaskTask.class);

        //遍历注册
        for(String key :map.keySet()){

            BaskTask baskTask = map.get(key);
            //baskTask:回调内部的run方法
            //baskTask.getCron() 获取各个任务的执行频率
            scheduledTaskRegistrar.addTriggerTask(baskTask,sheduledConfig->{
                Date date = new CronTrigger(baskTask.getCron()).nextExecutionTime(sheduledConfig);
                return date;
            });
        }
    } 
  
}

注意几个地方

  1. 添加@EnableScheduling注解,使得当前类支持定时任务

  2. 实现SchedulingConfigurer接口,重写configureTasks方法

  3. TaskOne添加了 @Data和字段cron,我们可以用一个接口把cron表达式替换掉,但是注意,需要在原先cron表达式作用下的逻辑运行到才会重新加载生效,所以不会马上修改当前任务,还会按照以前的业务逻辑执行之后再修改,也带来了一定的弊端。

private TaskOne taskOne;
//修改第一个任务提醒时间
@PostMapping("/updateTaskOneCron")
public Result<Boolean> updateTaskOneCron(@RequestParam("cron") String cron){
    taskOne.setCron(cron);
    return  Result.success(true);
}

我们用同样的方式增加TaskTwo...实现多定时任务。