前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot系列之动态生成cron表达式执行定时程序

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

作者头像
SmileNicky
发布2022-11-06 09:38:49
2.1K0
发布2022-11-06 09:38:49
举报
文章被收录于专栏:Nicky's blogNicky's blog

业务场景

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

在这里插入图片描述
在这里插入图片描述

环境准备

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

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

在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述

实现方案

可以分两步实现:

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

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

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

在这里插入图片描述
在这里插入图片描述

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

代码语言:javascript
复制
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();

}

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

代码语言:javascript
复制
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

代码语言:javascript
复制
<dependency>
     <groupId>org.redisson</groupId>
     <artifactId>redisson-spring-boot-starter</artifactId>
     <version>3.16.3</version>
 </dependency>

application.yml配置Redis

代码语言:javascript
复制
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: 123456
    database: 8

修改一下抽象类:

代码语言:javascript
复制
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();

}

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

代码语言:javascript
复制
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());
    }
}

归纳总结

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-10-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 业务场景
  • 环境准备
  • 实现方案
  • 归纳总结
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档