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

server/2025/2/1 13:05:09/

Spring Task

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

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

使用
1.添加依赖

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

java">        <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就可以了。

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

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

java">@Configuration
@EnableScheduling
public class SchedulingConfig implements SchedulingConfigurer {/*** 配置定时任务处理方式** @param taskRegistrar 定时任务注册器,用于管理和调度定时任务*/@Overridepublic 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表达式来灵活的设置任务调度的方式。

java">@Component
@ConditionalOnProperty(prefix = "scheduled",name = "task.enabled",havingValue = "true")//条件注解控制定时任务的启用和禁用
public class ScheduledTask {/*** 每5s执行一次*/@Scheduled(fixedRate = 5000)@TaskListenerpublic void performTask1(){System.out.println(getTime()+"执行定时任务1");}/*** 每分钟的第10s执行一次*/@Scheduled(cron = "10 * * * * ?")@TaskListenerpublic void performTask2(){System.out.println(getTime()+"执行定时任务2");}/*** 第一次执行任务前的延迟时间1s,后面每隔5s执行一次*/@Scheduled(initialDelay = 1000,fixedDelay = 5000)@TaskListenerpublic 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的方式实现任务监听器来进行监听处理。

java">@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TaskListener {
}
java">@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使用的是单一线程池,对于大量任务或长时间运行的任务,可能会导致阻塞,需要手动配置线程池参数(如核心线程数、最大线程数、队列大小等),并根据实际业务需要进行调优。


http://www.ppmy.cn/server/164071.html

相关文章

C#/.NET/.NET Core技术前沿周刊 | 第 23 期(2025年1.20-1.26)

前言 C#/.NET/.NET Core技术前沿周刊&#xff0c;你的每周技术指南针&#xff01;记录、追踪C#/.NET/.NET Core领域、生态的每周最新、最实用、最有价值的技术文章、社区动态、优质项目和学习资源等。让你时刻站在技术前沿&#xff0c;助力技术成长与视野拓宽。 欢迎投稿、推荐…

Deepseek本地部署(ollama+open-webui)

ollama 首先是安装ollama&#xff0c;这个非常简单 https://ollama.com/ 下载安装即可 open-webui 这个是为了提供一个ui&#xff0c;毕竟我们也不想在cmd和模型交互&#xff0c;很不方便。 第一&#xff0c;需要安装python3.11&#xff0c;必须是3.11&#xff08;其他版…

SG算法解析

Savitzky-Golay 滤波器的核心代码主要集中在计算投影矩阵B并使用这个矩阵对输入信号进行滤波。这部分核心代码包括计算B矩阵、处理边界效应和进行实际滤波操作。以下是对核心代码的一点解释&#xff1a; ① 计算 Savitzky-Golay 投影矩阵B B sgolay(order, framelen, weight…

解决.NET程序通过网盘传到Linux和macOS不能运行的问题

问题描述&#xff1a;.net程序用U盘传到虚拟机macOS和Linux可以正常运行&#xff0c;但是网盘传过去就不行。 解决方法&#xff1a; 这是文件权限的问题。当你通过U盘将文件传输到虚拟机的macOS和Linux系统时&#xff0c;文件的权限和所有权可能得到了保留或正确设置。但如果…

Git进阶之旅:分支管理策略

第一章&#xff1a; Git stash&#xff1a; 介绍&#xff1a; 当正在 dev 分支上开发某个项目&#xff0c;这时项目中出现一个 bug&#xff0c;需要修复&#xff0c;但是项目只完成一半&#xff0c;还不想提交&#xff0c;这时可以使用 git stash 命令将修改的内容保存至堆栈区…

从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础组件实现)

目录 基础组件实现 如何将图像和文字显示到OLED上 如何绘制图像 如何绘制文字 如何获取字体&#xff1f; 如何正确的访问字体 如何抽象字体 如何绘制字符串 绘制方案 文本绘制 更加方便的绘制 字体附录 ascii 6x8字体 ascii 8 x 16字体 基础组件实现 我们现在离手…

高频 SQL 50 题(基础版)_620. 有趣的电影

高频 SQL 50 题&#xff08;基础版&#xff09;_620. 有趣的电影 一级目录 表&#xff1a;cinema id 是该表的主键(具有唯一值的列)。 每行包含有关电影名称、类型和评级的信息。 评级为 [0,10] 范围内的小数点后 2 位浮点数。 编写解决方案&#xff0c;找出所有影片描述为 …

Google Protocol Buffers的.NET与Python

一、引言 大家好&#xff0c;我是 [博主名字]&#xff0c;一直致力于探索各种有趣且实用的技术&#xff0c;今天想和大家分享在项目开发中遇到的一个十分强大的工具 ——Google Protocol Buffers&#xff0c;以及它在.NET 与 Python 这两种不同语言环境中的应用和实践。 在当…