springboot2与定时任务的那点事

定时任务,顾名思义就是把任务定在某个时间点去做,可能是一次执行,也可能是循环往复,无论是那种形式,在如今都是非常的简单。

在Java中,Executors中的newSingleThreadScheduledExecutor(),以及Timer都可以实现定时功能,但是它们只能实现一些简单的定时任务,如果我们想实现类似cron表达式的复杂定时任务,它们就显得有些力不从心了。SpringBoot针对定时任务做了很好的封装,我们只需要进行简单的配置就可以实现。

简单的定时任务

SpringBoot实现一个简单的定时任务只需要两步,如下所示:

第一步,启动类上加上@EnableScheduling注解,

@SpringBootApplication(scanBasePackages = {"com.fanqie.springboot2"})
@EnableScheduling //表示开启定时任务
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

第二步:编写定时任务类,大家记得@Component注解一定要加上,我遇到身边的同事就是因为大意,没有加上该注解,导致SpringBoot不能扫描到该类,定时任务没有执行。

@Component
public class SimpleScheduler {
    /**
     * 每10秒执行一次定时任务
     */
    @Scheduled(cron = "*/10 * * * * ?")
    public void firstTask() {
        System.out.println("当前时间:" + LocalDateTime.now());
    }
}

到这里,我们就实现了一个简单的定时任务。@EnableScheduling 和@Scheduled,利用cron表达式,就可以实现各种复杂的定时任务。大家是不是觉得非常简单呢?是的,就是这么简单,利用SpringBoot提供的定时任务,我们就可以实现大部分的业务,不需要去额外配置quartz来完成,@Scheduled里面还包含了许多其它的参数,例如:fixedDelay(上一次任务执行完成后,再等待fixedDelay时长后,执行下一次任务)、fixedRate(任务执行时开始计时,若任务运行时长>fixedRate,下一次任务在上一次任务执行完成后立即执行)、initialDelay(初始化等待时间),为了防止篇幅过长,这里我就不写示例了,大家可以自行尝试一下。

现在可以完成大部分的业务需求了,但是现在,我们有这样的一个需求:在web端显示定时任务列表,并且可以实现定时任务的开启和关闭。大家思考两分钟...

定时任务的开启和关闭

大家可以发现,通过@EnableScheduling 和@Scheduled实现的定时任务,参数都是预先定义好的,而且我们不能控制它们的开启和关闭,当程序运行的时候,它们就已经启动了。我们不可能在开启的时候就把程序运行,关闭的时候就把程序杀掉吧,这样会让同行笑掉大牙的,而且还可能出现数据不一致的现象。

其实在SpringBoot中,ThreadPoolTaskScheduler就已经很好的集成了该功能,只需要我们写一些简单的代码,就可以实现自我控制,如何实现呢?请往下看:

第一步:ThreadPoolTaskScheduler配置,

@Configuration
public class SchedulerConfig {
    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setThreadFactory(r -> new Thread(r, "定时任务"));
        return threadPoolTaskScheduler;
    }
}

第二步:编写定时任务接口,这里为了演示方便,就不写web页面了,直接使用Swagger和List进行测试(正式项目中,直接替换为数据库或其它方式),

@RestController
@Api(tags = "定时任务模块")
@RequestMapping("/scheduler")
public class SchedulerController {
    private final ThreadPoolTaskScheduler threadPoolTaskScheduler;
    private List list = new ArrayList<>();

    /**
     * 默认启动一个定时任务
     *
     * @param threadPoolTaskScheduler threadPoolTaskScheduler
     */
    @Autowired
    public SchedulerController(ThreadPoolTaskScheduler threadPoolTaskScheduler) {
        this.threadPoolTaskScheduler = threadPoolTaskScheduler;
        //schedule有两个参数,Runnable task, Trigger trigger,task代表执行任务,trigger代表触发器
        String time = "*/10 * * * * ?";
        ScheduledFuture future1 = this.threadPoolTaskScheduler.schedule(() -> {
            Thread thread = Thread.currentThread();
            System.out.println(thread.getName() + thread.getId() + ": 我还在活着");
        }, new CronTrigger(time));
        //保存到集合中,
        SchedulerPO schedulerPO = new SchedulerPO(1, "我是定时任务一", "创建", future1, time);
        list.add(schedulerPO);
    }


    @ApiOperation(value = "关闭定时任务", notes = "关闭定时任务")
    @PutMapping("/{id}/cancel")
    public void cancelById(@PathVariable int id) {
        list.forEach(schedulerPO -> {
            if (schedulerPO.getId() == id) {
                ScheduledFuture future = schedulerPO.getScheduledFuture();
                if (future != null) {
                    future.cancel(true);
                }
            }
        });
    }

    @ApiOperation(value = "查询所有定时任务", notes = "查询所有定时任务")
    @GetMapping("/all")
    public List all() {
        return list;
    }

    @ApiOperation(value = "添加定时任务", notes = "添加定时任务")
    @PostMapping("/add")
    public List add(@RequestBody SchedulerPO schedulerPO) {
        ScheduledFuture future = this.threadPoolTaskScheduler.schedule(() -> {
            Thread thread = Thread.currentThread();
            System.out.println(thread.getName() + thread.getId() + ": 我还在活着");
        }, new CronTrigger(schedulerPO.getTime()));
        schedulerPO.setScheduledFuture(future);
        list.add(schedulerPO);
        return list;
    }
}

这里关于swagger的使用,就不进行说明了,如果大家不了解,可以看这篇springboot2与swagger2整合。现在我们就可以访问http://localhost:8080/swagger-ui.html来进行测试定时任务了,

springboot2与定时任务的那点事_第1张图片

首先查询所有定时任务,如下:

springboot2与定时任务的那点事_第2张图片

我们发现,当前有一个定时任务,scheduledFuture可以看出,这个定时任务正在运行。

现在,我们继续往其中添加一个定时任务(每隔2s执行一次),如下:

springboot2与定时任务的那点事_第3张图片

再来查询定时任务列表,如下:

springboot2与定时任务的那点事_第4张图片

我们可以发现,现在有两个定时任务在运行中,如果大家不相信它们在运行,也可以去跟踪后台的操作日志。两个定时任务好像有点多余了,我们关掉id为1的定时任务吧,如下:

springboot2与定时任务的那点事_第5张图片

查询定时任务列表,如下:

springboot2与定时任务的那点事_第6张图片

通过响应结果,我们可以看出,定时任务一已经被关闭了。到此为止,定时任务的启动和关闭就被我们实现了,SpringBoot已经帮我们整合了许多功能,有时候不用第三方工具,也可以实现常见的业务。在实际项目中,大家只需要把上面的List换成更加安全可靠的存储机制就可以了。

总结

如果大家只是实现简单的定时任务,@EnableScheduling 和@Scheduled就可以满足大家的需求(cron与fixedRate 、fixedDelay不能同时使用),当然,如果大家想要实现定时任务的自我控制,SpringBoot中的ThreadPoolTaskScheduler 就可以实现效果。

详细代码可见https://gitee.com/hpaw/SpringBoot2Demo/tree/master/springboot-scheduler

你可能感兴趣的:(SpringBoot)