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

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 定时任务注册器,用于管理和调度定时任务
     */
    @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表达式来灵活的设置任务调度的方式。

java 复制代码
@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);
    }
}

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

maven 复制代码
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使用的是单一线程池,对于大量任务或长时间运行的任务,可能会导致阻塞,需要手动配置线程池参数(如核心线程数、最大线程数、队列大小等),并根据实际业务需要进行调优。

相关推荐
吃货界的硬件攻城狮20 分钟前
【STM32 学习笔记】EXTI外部中断
笔记·stm32·学习
豆沙沙包?22 分钟前
2025年- H17-Lc125-73.矩阵置零(矩阵)---java版
java·线性代数·矩阵
anqi2727 分钟前
在sheel中运行Spark
大数据·开发语言·分布式·后端·spark
吃货界的硬件攻城狮33 分钟前
【STM32 学习笔记 】OLED显示屏及Keil调试
笔记·stm32·学习
程序员小刚1 小时前
基于SpringBoot + Vue 的作业管理系统
vue.js·spring boot·后端
njsgcs1 小时前
chili3d调试笔记12 deepwiki viewport svg雪碧图 camera three.ts
笔记
钦拆大仁1 小时前
华为云短信接入实现示例
java·华为云
FBI HackerHarry浩2 小时前
Linux云计算训练营笔记day02(Linux、计算机网络、进制)
linux·运维·网络·笔记·计算机网络·进制
问道飞鱼2 小时前
【Springboot知识】Springboot计划任务Schedule详解
java·spring boot·后端·schedule
北执南念2 小时前
IDEA回滚代码操作
android·java·intellij-idea