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

news2024/11/14 14:56:18

业务场景

最近需要实现一个功能,根据页面选择的星期,默认是凌晨执行,生成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表达之后,就可以实现定时程序了,可以根据模板方法设计模式,写一个抽象的类,封装一些通用的方法,给子类实现业务

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());
    }
}

归纳总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1925.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pycharm 如何自动调整 Python 代码符合 pep8 编码规范

前言 学生时代&#xff0c;写的一手漂亮的好字&#xff0c;能给人留下好的印象。作为 IT 人&#xff0c;写的一手漂亮的代码也会给人留下美好的印象。 代码就是自己的脸面&#xff0c;不管写质量怎样&#xff0c;首先要写的漂亮。Python 有一套 pep8 编码规范标准。 什么是 p…

电感重要参数的理解

电感作为一种储能元件&#xff0c;广泛运用在硬件电路的各个模块。较为常见的有DCDC电路&#xff0c;滤波电路以及振荡电路等。对于电感的选取&#xff0c;大多数人往往只关心感值&#xff0c;感值越大&#xff0c;储能越强&#xff0c;纹波也就越小。然而除了感值以外&#xf…

用Python剪辑视频?太简单了

人生苦短&#xff0c;快学Python&#xff01; 最近我在网上下载一个视频&#xff0c;结果下载到本地是近百个视频片段&#xff0c;为了方便观看只能将这些片段合并为一个视频整体。 不过我并没有搜到能够处理类似情况的小工具&#xff0c;只是发现剪映等软件可以实现视频合并功…

Allegro DFM Ravel Rule丝印文字到测试点pad间距检查

Allegro DFM Ravel Rule丝印文字到测试点pad间距检查 下面介绍丝印到测试点pad间距检查 设置top层丝印文字到孔属性测试点间距,默认值是1mil,可以自己修改这个值 设置bottom层丝印文字到孔属性测试点间距 设置top层丝印文字到通孔pin属性测试点间距 设置bottom层丝印文字…

VAEGAN:理解 VAE 与 GAN【图像生成】

标准VAE(Variational Autoencoder)的原理&#xff1a; 在autoencoder模型中&#xff0c;我们加入一个编码器&#xff0c;它能帮我们把图片编码成向量。然后解码器能够把这些向量恢复成图片。 标准自编码器我们现在获得了一个有点实际用处的网络了。而且我们现在能训练任意多的…

mulesoft Module 4 quiz解析

mulesoft Module 4 quiz1. What is NOT part of a Mule 4 event?2. A Database connector is configured to select rows from a Mysql database.3. What is the minimue required configuration in a flow for a Mule application to compile?4. What is the purpose of the…

Qt读写Excel文件与QXlsx的使用

最近项目比较忙&#xff0c;许久没写博客了&#xff0c;想着还是需要定期整理下学到的和用到的新东西&#xff0c;才有沉淀。刚好最近使用Qt时需要读取excel文件的数据&#xff0c;于是在github找了一个开源库QXlsx&#xff0c;Star数还比较多&#xff0c;应该靠谱&#xff0c;…

【CV】第 2 章:使用本地二进制模式的内容识别

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

MPLS隧道——PE与CE之间运行不同路由协议的情况分析

目录 PE和CE之间运行OSPF路由协议 Ospf的Dmian ID&#xff08;还原属性&#xff09; Ospf的Sham Link属性 OSPF的DN置位与VPN Router Tag PE和CE之间运行BGP路由协议 AS号替换功能 BGP的SoO属性 PE和CE之间可以不同部署不同的路由协议&#xff0c;部署不同的路由协议会存…

量子笔记:多比特量子门

目录 0. 前言 2. 量子多重H门 3. 量子SWAP门 4. 量子CNOT门&#xff08;CX门&#xff09;&#xff1a;受控非门 5. 量子CY和CZ门 6. 量子C*门 6.1 受控S门 6.2 受控H门 6.3 受控Z旋转门 6.4 受控X旋转门 6.4 受控Y旋转门 7. 量子托佛利门(CCNOT门) 8. 量子弗雷德…

【Linux】Linux中的gcc/g++编译器的使用

&#x1f4ac;推荐一款模拟面试、刷题神器 、从基础到大厂面试题&#xff1a;&#x1f449;点击跳转刷题网站进行注册学习 目录 一、编译的过程 1、预处理阶段 1.1预处理的工作——头文件展开、去注释、宏替换、条件编译 1.2外部定义宏&#xff08;-D选项&#xff09; 1.…

记一次网络安全应急响应流程

0x01 事件分析 网站源码被篡改&#xff0c;攻击者一定获取到了权限&#xff0c;那么接下来的思路就是推测攻击者入侵手段&#xff0c;找到业务脆弱点&#xff0c;对服务器进行全方位排查&#xff0c;找到攻击者留下来的痕迹并进行分析处理。 2.1 信息收集 与客户简单沟通后&…

【C++】动态联编、delete/free【有无析构】的使用,虚析构

文章目录动态联编的条件&#xff1a;联编的概念&#xff1a;1. 动态联编&#xff1a;2. 静态联编&#xff1a;静态联编时确认了那些属性&#xff1a;结论&#xff1a;基类指针和派生类指针访问虚函数结论&#xff1a;delete和free的使用条件&#xff1a;1. 没有析构函数时&…

由一个按键程序引发的思考(上)

说起按键程序&#xff0c;只要会单片机的肯定都很熟悉。一般开始学习单片机的时候&#xff0c;入门程序基本都是LED灯和按键。那么这个按键程序有什么特别的吗&#xff0c;还需要专门去思考吗&#xff1f;如果我刚开始学单片机的时候也会这么想&#xff0c;但是随着项目的积累&…

微信早安消息推送(大升级版-企业微信)

微信早安消息推送&#xff08;企业微信&#xff09; 在微信公众号推送的基础上的一个升级 新增图文、疫情状况等 可以修改头像 、 昵称 效果展示 更多资讯 源代码获取 → 薇信公粽号“Cloud技术栈”&#xff0c;回复“企业微信推送” ———————————————————…

【贪心算法-LeetCode3:无重复字符的最长子串(Java实现)】

无重复字符的最长子串一、题目描述1.题目内容2.样例二、解决方案1.算法流程1&#xff09;分析2&#xff09;算法流程2.Java代码1&#xff09;核心代码2&#xff09;完整测试代码个人社区&#xff1a;https://bbs.csdn.net/forums/smile 个人主页&#xff1a;https://blog.csdn.…

STM32G070RBT6基于STM32CubeMX创建定时器中断控制LED闪烁

STM32G070RBT6基于STM32CubeMX创建定时器中断控制LED闪烁&#x1f4cd;相关篇《【硬件开源电路】STM32G070RBT6开发板》&#x1f33a;配置内容演示&#xff1a; &#x1f4da;功能介绍 &#x1f4d1;通过STM32CubeMX配置定时器1和定时器3分别作为两个led的定时闹钟。这里作为…

单片机原理及应用实验一交通信号系统(基于Proteus仿真)

1.实验内容与要求 交通信号系统是保障交通安全高效的重要设施。51单片机价格低廉、体积小、低功耗、抗干扰性好等优点&#xff0c;适用于交通信号系统中&#xff0c;本实验利用51单片机IO口的开关量的输入输出功能及内部CPU运算功能&#xff0c;设计一个简易的交通信号灯系统&…

生成.keystore 安卓签名

需要有openssl.exe和keytool.exe支持 signapk.jar手动签名命令 java -jar signapk.jar pl.x509.pem pl.pk8 smartrecord_3.5.8.apk smartrecord_3.5.8-signed.apk除了直接使用signapk.jar签名外&#xff0c;还可以将签名文件生成keystore文件&#xff0c;然后配置编译器给apk进…

数据分析 | Pandas 200道练习题,每日10道题,学完必成大神(5)

文章目录前期准备1. 将create Time列设置为索引2. 生成一个和df长度相同的随机数DataFrame3. 将上一题生成的DataFrame与df合并4. 生成的新的一列new值为salary列减去之前生成的随机数列5. 检查数据中是否含有空值6. 将salary类型转换成浮点数7. 计算salary 大于10000的次数8. …