Java定时任务实现方案(四)——Spring Task

Spring Task

这篇笔记,我们要来介绍实现Java定时任务的第四个方案,使用Spring Task,以及该方案的优点和缺点。

​ Spring Task是Spring框架提供的一个轻量级任务调度框架,用于简化任务调度的开放,通过注解或XML配置的方式,可以轻松实现定时任务、异步任务等功能,我们这里主要介绍它的定时任务。

使用
1.添加依赖

​ 既然要使用Spring框架提供的SpringTask,我们就要引入相关的依赖,同时,我们后面要通过自定义注解+AOP的方式实现任务监听,所以也要引入SpringAop的相关依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>3.3.0</version>
        </dependency>
2.启用定时任务支持

​ 要使用SpringTask实现定时任务,我们首先要启用定时任务支持,这个我们只需要在Spring应用的启动类上加上@EnableScheduling就可以了。

@SpringBootApplication
@EnableScheduling
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}
3.配置定时任务注册器

​ 我们可以通过配置类,对SpringTask执行定时任务的调度器进行相关的配置和初始化,并注册到Spring容器中,便于统一管理调度任务。

@Configuration
@EnableScheduling
public class SchedulingConfig implements SchedulingConfigurer {
    /**
     * 配置定时任务处理方式
     *
     * @param taskRegistrar 定时任务注册器,用于管理和调度定时任务
     */
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 创建一个线程池任务调度器
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        //设置线程池大小
        taskScheduler.setPoolSize(10);
        // 设置线程名称前缀,便于识别和管理
        taskScheduler.setThreadNamePrefix("perform-");
        // 初始化线程池任务调度器
        taskScheduler.initialize();
        // 将任务调度器设置到定时任务注册器中,以便统一管理调度任务
        taskRegistrar.setTaskScheduler(taskScheduler);

    }
}
4.创建定时任务

​ 接着,我们就可以编写定时任务了,这里我们采用注解的形式提供示例。只需要在外面的定时方法上面加上一个@Scheduled注解,Spring容器就可以帮我们实现定时任务了。这个@Scheduled注解可以设置一些参数便于更灵活的调度任务,例如fixedRate参数可以让我们设置间隔多长时间执行一次、initialDelay参数可以设置第一次执行任务前延迟的时间,fixedDelay参数也可以设置每一次延迟多长时间再执行定时任务。同时,该注解还支持使用cron表达式来灵活的设置任务调度的方式。

@Component
@ConditionalOnProperty(prefix = "scheduled",name = "task.enabled",havingValue = "true")//条件注解控制定时任务的启用和禁用
public class ScheduledTask {
    /**
     * 每5s执行一次
     */
    @Scheduled(fixedRate = 5000)
    @TaskListener
    public void performTask1(){
        System.out.println(getTime()+"执行定时任务1");
    }

    /**
     * 每分钟的第10s执行一次
     */
    @Scheduled(cron = "10 * * * * ?")
    @TaskListener
    public void performTask2(){
        System.out.println(getTime()+"执行定时任务2");
    }

    /**
     * 第一次执行任务前的延迟时间1s,后面每隔5s执行一次
     */
    @Scheduled(initialDelay = 1000,fixedDelay = 5000)
    @TaskListener
    public void performTask3(){
        System.out.println(getTime()+"执行定时任务3");
    }
    /**
     * 获取当前系统时间
     * @return
     */
    public static String getTime(){
        //获取当前的系统时间
        LocalDateTime now = LocalDateTime.now();
        //定义时间格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        //格式化时间
        return now.format(formatter);
    }
}

​ 这里需要补充的是,我们可以使用条件注解来控制定时任务的启动或禁止。

scheduled:
  task:
    enabled: true
补充知识点:cron表达式

​ cron表达式是一种用于指定定时任务执行时间的字符串格式,广泛用于Linux系统的cron作业调度器以及各种编程框架中。

​ 一个标准的CRON表达式由6个或7个字段组成(取决于是否包含年份字段),各字段之间用空格分隔:[秒] [分钟] [小时] [日期] [月份] [星期] [年份] (可选)

​ 各字段的取值范围如下:

字段范围
0-59
分钟0-59
小时0-23
日期1-31
月份1-12或JAN-DEC
星期0-7或SUN-SAT(0和7都表示星期日)
年份(可选)1970-2099

​ 常见符号说明:

符号含义
*表示该字段的所有可能值,例如*在分钟字段表示每分钟
,分隔多个具体的值,例如1,15表示第1分钟和第15分钟
-定义一个值范围,例如10-15表示从第10分钟到第15分钟
/指定增量,例如*/5表示每隔5个单位执行一次
?用于日期或星期字段,表示不指定明确的值,通常用于其中一个字段时,另一个字段有具体值
L表示最后一天或最后一个工作日,例如L在日期字段表示每月最后一天。
W表示最近的工作日,例如15W表示离15号最近的工作日
#用于星期字段,表示某个月的第几个星期几,例如2#3表示每月的第三个星期二

​ 注意:cron表达式的星期和日期不能同时为具体的值,如果同时指定具体的值,可能会导致逻辑冲突或不明确的行为。

​ 示例:

cron表达式含义
0 30 * * * ?每小时第30分钟执行
0 0 2 * * ?每天凌晨2点执行
0 0 8 ? * MON每周一的上午8点执行
0 0 0 L * ?每月的最后一天的午夜执行
0 0/5 * * * ?每5分钟执行一次
5.通过自定义注解+AOP实现任务监听(可选)

​ 如果需要对定时任务进行监听并做出其他相应的处理的话,我们还可以自己通过自定义注解+AOP的方式实现任务监听器来进行监听处理。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TaskListener {
}
@Component
@Aspect
public class TaskListenerAspect {
    @Before("@annotation(xiaoxin.Timer.SpringTask.TaskListener)")
    public void beforeTask(JoinPoint joinPoint){
        System.out.println("task:"+joinPoint.getSignature().getName()+" 即将开始...");
    }
    @After("@annotation(xiaoxin.Timer.SpringTask.TaskListener)")
    public void afterTask(JoinPoint joinPoint){
        System.out.println("task:"+joinPoint.getSignature().getName()+" 已结束...");
    }
}
优点
1.简单易用

​ Spring Task提供了非常简洁的API,通过@Scheduled注解即可轻松定义定时任务,无需复杂的配置,对于简单的定时任务需求,开发者可以快速上手并实现。

2.集成方便

​ Spring Task与Spring框架无缝集成,可以直接利用Spring的依赖注入、事务管理等功能,可以直接在Spring容器管理的Bean中定义定时任务方法,减少额外的配置和代码量。

3.灵活性高

​ Spring Task支持多种调度方式,包括固定延迟(fixedDelay)、固定速率(fixedRate)以及cron表达式,可以根据实际需求灵活选择。还可以结合AOP和自定义注解,添加日志记录和性能监控,增添定时任务的功能。

4.轻量级

​ Spring Task不需要引入额外的重量级调度框架(如Quartz),适合中小型项目或对调度功能要求不高的场景,减少了项目依赖的复杂度和维护成本。

5.线程池支持

​ Spring Task内置线程池支持,可以通过配置文件或Java配置类调整线程池大小,优化任务执行效率,适用与并发执行多个定时任务的场景,确保任务不会因为线程资源不足而阻塞。

6.易于测试

​ 定时任务方法是普通的Java方法,可以通过单元测试工具进行测试。

缺点
1.调度精度有限

​ Spring Task 的调度依赖于JVM线程调度器,因此在高并发或系统负载较高的情况下,可能会出现调度延迟,对于需要极高精度的任务(如毫秒级),Spring Task可能无法满足需求。

2.缺乏分布式支持

​ Spring Task本身不支持分布式调度,如果应用程序部署在多个节点上,每个节点都会独立执行定时任务,可能导致任务重复执行,因此需要额外引入分布式锁机制(如Redis、数据库等)来确保任务只在一个节点上执行。

3.配置灵活性不足

​ 虽然Spring Task可以通过cron表达式实现复杂的调度逻辑,但对于更复杂的调度需求(如动态调整任务执行时间),Spring Task 的配置显得不够灵活,动态修改任务调度规则时,通常需要重启应用或手动触发重新加载配置。

4.错误处理和重试机制较弱

​ Spring Task没有内置的任务失败重试机制,如果任务执行过程中发生异常,默认情况下不会自动重试,需要开发者自行实现错误处理和重试逻辑,增加了开发的复杂度。

5.监控和管理功能较弱

​ Spring Task缺乏内置的任务监控和管理功能,难以实时查看任务的执行状态和历史记录等信息。

6.线程池配置复杂

​ 默认情况下,Spring Task使用的是单一线程池,对于大量任务或长时间运行的任务,可能会导致阻塞,需要手动配置线程池参数(如核心线程数、最大线程数、队列大小等),并根据实际业务需要进行调优。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是循环神经网络?

一、概念 循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;是一类用于处理序列数据的神经网络。与传统的前馈神经网络不同&#xff0c;RNN具有循环连接&#xff0c;可以利用序列数据的时间依赖性。正因如此&#xff0c;RNN在自然语言处理、时间序列预测、语…

Python设计模式 - 组合模式

定义 组合模式&#xff08;Composite Pattern&#xff09; 是一种结构型设计模式&#xff0c;主要意图是将对象组织成树形结构以表示"部分-整体"的层次结构。这种模式能够使客户端统一对待单个对象和组合对象&#xff0c;从而简化了客户端代码。 组合模式有透明组合…

19.Word:小马-校园科技文化节❗【36】

目录 题目​ NO1.2.3 NO4.5.6 NO7.8.9 NO10.11.12索引 题目 NO1.2.3 布局→纸张大小→页边距&#xff1a;上下左右插入→封面&#xff1a;镶边→将文档开头的“黑客技术”文本移入到封面的“标题”控件中&#xff0c;删除其他控件 NO4.5.6 标题→原文原文→标题 正文→手…

一文讲解Java中Object类常用的方法

在Java中&#xff0c;经常提到一个词“万物皆对象”&#xff0c;其中的“万物”指的是Java中的所有类&#xff0c;而这些类都是Object类的子类&#xff1b; Object主要提供了11个方法&#xff0c;大致可以分为六类&#xff1a; 对象比较&#xff1a; public native int has…

多项日常使用测试,带你了解如何选择AI工具 Deepseek VS ChatGpt VS Claude

多项日常使用测试&#xff0c;带你了解如何选择AI工具 Deepseek VS ChatGpt VS Claude 注&#xff1a;因为考虑到绝大部分人的使用&#xff0c;我这里所用的模型均为免费模型。官方可访问的。ChatGPT这里用的是4o Ai对话&#xff0c;编程一直以来都是人们所讨论的话题。Ai的出现…

Linux下学【MySQL】表的必备操作( 配实操图和SQL语句)

绪论​ “Patience is key in life &#xff08;耐心是生活的关键&#xff09;”。本章是MySQL中非常重要且基础的知识----对表的操作。再数据库中表是存储数据的容器&#xff0c;我们通过将数据填写在表中&#xff0c;从而再从表中拿取出来使用&#xff0c;本章主要讲到表的增…

【Java数据结构】了解排序相关算法

基数排序 基数排序是桶排序的扩展&#xff0c;本质是将整数按位切割成不同的数字&#xff0c;然后按每个位数分别比较最后比一位较下来的顺序就是所有数的大小顺序。 先对数组中每个数的个位比大小排序然后按照队列先进先出的顺序分别拿出数据再将拿出的数据分别对十位百位千位…

【全栈】SprintBoot+vue3迷你商城(9)

【全栈】SprintBootvue3迷你商城&#xff08;9&#xff09; 往期的文章都在这里啦&#xff0c;大家有兴趣可以看一下 后端部分&#xff1a; 【全栈】SprintBootvue3迷你商城&#xff08;1&#xff09; 【全栈】SprintBootvue3迷你商城&#xff08;2&#xff09; 【全栈】Spr…

php-phar打包避坑指南2025

有很多php脚本工具都是打包成phar形式&#xff0c;使用起来就很方便&#xff0c;那么如何自己做一个呢&#xff1f;也找了很多文档&#xff0c;也遇到很多坑&#xff0c;这里就来总结一下 phar安装 现在直接装yum php-cli包就有phar文件&#xff0c;很方便 可通过phar help查看…

【数据结构】_顺序表

目录 1. 概念与结构 1.1 静态顺序表 1.2 动态顺序表 2. 动态顺序表实现 2.1 SeqList.h 2.2 SeqList.c 2.3 Test_SeqList.c 3. 顺序表性能分析 线性表是n个具有相同特性的数据元素的有限序列。 常见的线性表有&#xff1a;顺序表、链表、栈、队列、字符串等&#xff1b…

OPencv3.4.1安装及配置教程

来到GitHub上opencv的项目地址 https://github.com/opencv/opencv/releases/tag/3.4.1 以上资源包都是 OpenCV 3.4.1 版本相关资源&#xff0c;它们的区别如下&#xff1a; (1). opencv-3.4.1-android-sdk.zip&#xff1a;适用于 Android 平台的软件开发工具包&#xff08;SDK…

世上本没有路,只有“场”et“Bravo”

楔子&#xff1a;电气本科“工程电磁场”电气研究生课程“高等电磁场分析”和“电磁兼容”自学”天线“、“通信原理”、“射频电路”、“微波理论”等课程 文章目录 前言零、学习历程一、Maxwells equations1.James Clerk Maxwell2.自由空间中传播的电磁波3.边界条件和有限时域…

ZYNQ-IP-AXI-GPIO

AXI GPIO 可以将 PS 端的一个 AXI 4-Lite 接口转化为 GPIO 接口&#xff0c;并且可以被配置为单端口或双端口&#xff0c;每个通道的位宽可以独立配置。 通过使能三态门可以将端口动态地配置为输入或输出。 AXIGPIO 是 ZYNQ PL 端的一个 IP 核&#xff0c;可以将 AXI-Lite Mas…

20.Word:小谢-病毒知识的科普文章❗【38】

目录 题目​ NO1.2.3文档格式 NO4.5 NO6.7目录/图表目录/书目 NO8.9.10 NO11索引 NO12.13.14 每一步操作完&#xff0c;确定之后记得保存最后所有操作完记得再次删除空行 题目 NO1.2.3文档格式 样式的应用 选中应用段落段落→开始→选择→→检查→应用一个一个应用ctr…

为什么应用程序是特定于操作系统的?[计算机原理]

你把WINDOWS程序复制到MAC上使用&#xff0c;会发现无法运行。你可能会说&#xff0c;MAC是arm处理器&#xff0c;而WINDWOS是X86 处理器。但是在2019年&#xff0c;那时候MAC电脑还全是Intel处理器&#xff0c;在同样的X86芯片上&#xff0c;运行MAC和WINDOWS 程序还是无法互相…

LigerUI在MVC模式下的响应原则

LigerUI是基于jQuery的UI框架&#xff0c;故他也是遵守jQuery的开发模式&#xff0c;但是也具有其特色的侦听函数&#xff0c;那么当LigerUI作为View层的时候&#xff0c;他所发送后端的必然是表单的数据&#xff0c;在此我们以俩个div为例&#xff1a; {Layout "~/View…

BurpSuite--暴力破解

一.弱口令 1. 基本概念 介绍&#xff1a;弱口令&#xff08;weak password&#xff09;是指那些容易被他人猜测或通过工具破解的密码。虽然弱口令没有严格的定义&#xff0c;但通常它指的是由简单的数字、字母、常用词语或规律性组合构成的密码。 特点&#xff1a; 密码容易被…

深入探讨防抖函数中的 this 上下文

深入剖析防抖函数中的 this 上下文 最近我在研究防抖函数实现的时候&#xff0c;发现一个耗费脑子的问题&#xff0c;出现了令我困惑的问题。接下来&#xff0c;我将通过代码示例&#xff0c;深入探究这些现象背后的原理。 示例代码 function debounce(fn, delay) {let time…

【PostgreSQL内核学习 —— (WindowAgg(一))】

WindowAgg 窗口函数介绍WindowAgg理论层面源码层面WindowObjectData 结构体WindowStatePerFuncData 结构体WindowStatePerAggData 结构体eval_windowaggregates 函数update_frameheadpos 函数 声明&#xff1a;本文的部分内容参考了他人的文章。在编写过程中&#xff0c;我们尊…

RocketMQ消息是如何存储的?

大家好&#xff0c;我是锋哥。今天分享关于【RocketMQ消息是如何存储的&#xff1f;】面试题。希望对大家有帮助&#xff1b; RocketMQ消息是如何存储的&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 RocketMQ 使用了一个高性能、分布式的消息存储架构…