IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringBoot系列之动态生成cron表达式执行定时程序 -> 正文阅读

[Java知识库]SpringBoot系列之动态生成cron表达式执行定时程序

业务场景

最近需要实现一个功能,根据页面选择的星期,默认是凌晨执行,生成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());
    }
}

归纳总结

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

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:40:46  更:2022-10-31 11:41:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 11:36:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码