SpringBoot系列之动态定时程序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot系列之动态定时程序相关的知识,希望对你有一定的参考价值。


业务场景

最近需要实现一个功能,根据页面选择的星期,默认是凌晨执行,生成cron表达式,然后定时执行定时程序

SpringBoot系列之动态定时程序_spring

环境准备

  • 开发环境
  • JDK 1.8
  • SpringBoot2.2.1
  • Maven 3.2+
  • 开发工具
  • IntelliJ IDEA
  • smartGit
  • Navicat15

在IDEA里集成阿里的​​https://start.aliyun.com​​​,创建一个​​Spring Initializr​​项目:

SpringBoot系列之动态定时程序_mybatis_02


选择jdk版本,和maven打包方式,选择需要的​​dependencies​

SpringBoot系列之动态定时程序_java_03

实现方案

可以分两步实现:

  1. 先根据选择的星期生成cron表达式,保存到数据库里
  2. 根据保存的cron表达式规则执行定时程序

生成cron表达式的可以写一些工具类,网上教程比较多,可以参考网上教程:​​Java生成cron表达式工具类​​

生成cron表达式之后,保存到数据库里即可

SpringBoot系列之动态定时程序_spring_04


有了动态配置的cron表达之后,就可以实现定时程序了,可以根据模板方法设计模式,写一个抽象的类,封装一些通用的方法,给子类实现业务

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;

@Slf4j
public abstract class AbstractScheduler implements SchedulingConfigurer

@Override
public void configureTasks(ScheduledTaskRegistrar registrar)
registrar.addTriggerTask(()->
// 执行业务
doBusiness();
, triggerContext ->
String cron = this.getCronString();
CronTrigger trigger;
try
trigger = new CronTrigger(cron);
return trigger.nextExecutionTime(triggerContext);
catch (Exception e)
log.error("cron表达式异常,已经启用默认配置");
// 配置cron表达式异常,执行默认的表达式
trigger = new CronTrigger(getDefaultCron());
return trigger.nextExecutionTime(triggerContext);

);

// 获取cron表达式方法,抽象方法,给子类实现
protected abstract String getCronString();
// 执行业务操作
protected abstract void doBusiness();
// cron表达式报错获取默认的cron表达式
protected abstract String getDefaultCron();

子类实现抽象类,然后实现方法:

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service
@Slf4j
public class ItemSyncScheduler extends AbstractScheduler

// 默认的cron表达式
@Value("$configtask.default.itemsync")
private String defaultCron ;

@Override
protected String getCronString()
// 获取数据库里的cron表达式
String cronString = "0 0/1 * * * ?";
return cronString ;


@Override
protected void doBusiness()
// 执行业务操作


@Override
protected String getDefaultCron()
return defaultCron;

看起来是没多大问题,不过在定时程序,分布式环境,可能会出现重复执行业务的情况,所以需要加上分布式锁,可以直接使用redission的分布式锁

加上​​redisson-spring-boot-starter​

<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.16.3</version>
</dependency>

application.yml配置Redis

spring:
redis:
host: 127.0.0.1
port: 6379
password: 123456
database: 8

修改一下抽象类:

import cn.hutool.core.thread.NamedThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.locks.Lock;

@Slf4j
public abstract class AbstractScheduler implements SchedulingConfigurer, InitializingBean

private Lock rlock;

@Override
public void afterPropertiesSet()
rlock = getLock();


@Override
public void configureTasks(ScheduledTaskRegistrar registrar)
registrar.addTriggerTask(()->
try
// 加分布式锁
rlock.lock();
// 执行业务
doBusiness();
finally
// 释放锁
rlock.unlock();

, triggerContext ->
String cron = this.getCronString();
CronTrigger trigger;
try
trigger = new CronTrigger(cron);
return trigger.nextExecutionTime(triggerContext);
catch (Exception e)
log.error("cron表达式异常,已经启用默认配置");
// 配置cron表达式异常,执行默认的表达式
trigger = new CronTrigger(getDefaultCron());
return trigger.nextExecutionTime(triggerContext);

);


protected abstract String getCronString();

protected abstract void doBusiness();

protected abstract String getDefaultCron();

protected abstract Lock getLock();

子类可以根据需要选择锁,可以是单机锁或者分布式锁

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service
@Slf4j
public class ItemSyncScheduler extends AbstractScheduler

@Autowired
private RedissonClient redissonClient;

// 默认的cron表达式
@Value("$configtask.default.itemsync")
private String defaultCron ;

@Override
protected String getCronString()
// 获取数据库里的cron表达式
String cronString = "0 0/1 * * * ?";
return cronString ;


@Override
protected void doBusiness()
// 执行业务操作


@Override
protected String getDefaultCron()
return defaultCron;


@Override
protected Lock getLock()
return redissonClient.getLock(this.getClass().getSimpleName());

归纳总结

项目里合理使用设计模式可以提高代码复用行,代码拓展行好,看起来比较简洁


以上是关于SpringBoot系列之动态定时程序的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot系列之动态定时程序

SpringBoot系列之动态生成cron表达式执行定时程序

SpringBoot系列之动态定时程序改进版

SpringBoot系列之动态定时程序改进版

SpringBoot系列之动态定时程序改进版

SpringBoot之scheduled定时器