Spring Boot 定时任务的多种实现方式

前言

欢迎来到我的技术小宇宙! 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。

  • 洛可可白:个人主页

  • 个人专栏:✅前端技术 ✅后端技术

  • 个人博客:洛可可白博客

  • 代码获取:bestwishes0203

  • 封面壁纸:洛可可白wallpaper

在这里插入图片描述

这里写自定义目录标题

  • Spring Boot 定时任务的多种实现方式
    • 1. 使用 `@Scheduled` 注解
      • 示例代码
      • 优点
      • 缺点
    • 2. 使用 `ScheduledExecutorService`
      • 示例代码
      • 优点
      • 缺点
    • 3. 使用 Quartz 框架
      • 示例代码
      • 优点
      • 缺点
    • 4. 使用 `SchedulingConfigurer` 接口
      • 示例代码
      • 优点
      • 缺点
    • 5. 使用 `TaskScheduler` 接口
      • 示例代码
      • 优点
      • 缺点
    • 6. 使用 Redis 实现分布式定时任务
      • 示例代码
      • 优点
      • 缺点
    • 7. 使用 XXL-JOB 或 Elastic-Job
      • 示例代码(XXL-JOB)
      • 优点
      • 缺点
    • 总结
    • 选择合适的定时任务实现方式
    • 示例项目结构
    • 示例代码完整版
      • `DemoApplication.java`
      • `SchedulingConfig.java`
      • `ScheduledTask.java`
      • `RedisConfig.java`
      • `RedisDistributedTask.java`
      • `application.properties`
    • 结语

Spring Boot 定时任务的多种实现方式

在现代的 Spring Boot 应用程序中,定时任务是一种常见的需求。无论是定期清理日志、同步数据,还是执行定时的业务逻辑,Spring Boot 提供了多种灵活的方式来实现定时任务。本文将详细介绍几种常见的定时任务实现方式,并通过示例代码帮助你快速上手。

1. 使用 @Scheduled 注解

@Scheduled 是 Spring Boot 提供的一种简单且强大的定时任务实现方式,适用于大多数简单的定时任务场景。

示例代码

配置类

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@EnableScheduling
public class SchedulingConfig {
    // 启用定时任务支持
}

任务类

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

@Component
public class ScheduledTask {
    // 固定频率:每5秒执行一次任务
    @Scheduled(fixedRate = 5000)
    public void executeTask() {
        System.out.println("【@Scheduled 任务】执行时间:" + LocalDateTime.now());
    }

    // Cron 表达式:每天中午12点执行一次任务
    @Scheduled(cron = "0 0 12 * * ?")
    public void executeCronTask() {
        System.out.println("【Cron 任务】执行时间:" + LocalDateTime.now());
    }
}

优点

  • 简单易用:只需添加注解即可实现定时任务。
  • 集成度高:与 Spring 容器无缝集成,支持依赖注入。

缺点

  • 单节点运行:默认情况下,@Scheduled 任务仅在单个节点上运行,不支持分布式场景。

2. 使用 ScheduledExecutorService

ScheduledExecutorService 是 Java 提供的原生定时任务工具,适合需要更细粒度控制的任务调度场景。

示例代码

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledExecutorServiceExample {
    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        executor.scheduleAtFixedRate(() -> {
            System.out.println("任务执行:" + System.currentTimeMillis());
        }, 0, 5, TimeUnit.SECONDS);
    }
}

优点

  • 灵活性高:可以自定义线程池大小和任务执行策略。
  • 独立性强:不依赖于 Spring 框架,适合非 Spring 环境。

缺点

  • 功能有限:不支持复杂的时间调度策略,如 Cron 表达式。

3. 使用 Quartz 框架

Quartz 是一个功能强大的任务调度框架,支持复杂的调度任务,例如动态任务配置和分布式任务调度。

示例代码

依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-quartzartifactId>
dependency>

任务类

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("Quartz 任务执行:" + System.currentTimeMillis());
    }
}

配置类

import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class QuartzConfig {
    @Bean
    public JobDetail jobDetail() {
        return JobBuilder.newJob(MyJob.class)
                .withIdentity("myJob")
                .storeDurably()
                .build();
    }

    @Bean
    public Trigger jobTrigger(JobDetail jobDetail) {
        return TriggerBuilder.newTrigger()
                .forJob(jobDetail)
                .withIdentity("myTrigger")
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(5)
                        .repeatForever())
                .build();
    }
}

优点

  • 功能强大:支持复杂的调度策略,如动态任务配置和分布式任务调度。
  • 高可靠性:支持任务持久化和恢复。

缺点

  • 配置复杂:需要额外的配置和依赖。
  • 学习曲线:相比 @Scheduled,Quartz 的学习成本较高。

4. 使用 SchedulingConfigurer 接口

SchedulingConfigurer 是 Spring 提供的一个接口,允许动态配置定时任务,例如从数据库中读取任务配置。

示例代码

import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Date;

@Configuration
public class DynamicSchedulingConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.initialize();
        taskRegistrar.setTaskExecutor(executor);

        taskRegistrar.addTriggerTask(
                () -> System.out.println("动态任务执行:" + new Date()),
                new Trigger() {
                    @Override
                    public Date nextExecutionTime(TriggerContext triggerContext) {
                        return new CronTrigger("0/5 * * * * ?").nextExecutionTime(triggerContext);
                    }
                }
        );
    }
}

优点

  • 动态配置:支持从外部动态加载任务配置。
  • 灵活性高:可以结合数据库或其他存储实现动态任务管理。

缺点

  • 复杂度高:实现动态任务配置需要额外的开发工作。

5. 使用 TaskScheduler 接口

TaskScheduler 是 Spring 提供的一个接口,支持动态任务调度,适合需要动态调整任务执行频率的场景。

示例代码

import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Date;

@Configuration
@EnableScheduling
public class TaskSchedulerConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.initialize();
        taskRegistrar.setTaskExecutor(executor);

        taskRegistrar.addFixedRateTask(() -> System.out.println("固定频率任务执行:" + new Date()), 5000);
    }
}

优点

  • 动态调度:支持动态调整任务执行频率。
  • 简单易用:与 Spring 容器无缝集成。

缺点

  • 功能有限:不支持复杂的时间调度策略。

6. 使用 Redis 实现分布式定时任务

Redis 是一个高性能的键值存储系统,可以利用其特性实现简单的分布式定时任务。

示例代码

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class RedisDistributedTask {
    private final RedisTemplate<String, String> redisTemplate;

    public RedisDistributedTask(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Scheduled(fixedRate = 5000)
    public void executeTask() {
        String lockKey = "distributedTaskLock";
        String lockValue = String.valueOf(System.currentTimeMillis());
        Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS);
        if (lock != null && lock) {
            System.out.println("分布式任务执行:" + new Date());
        }
    }
}

优点

  • 分布式支持:可以实现简单的分布式任务调度。
  • 简单易用:基于 Redis 的特性,实现简单。

缺点

  • 功能有限:不支持复杂的调度策略。

7. 使用 XXL-JOB 或 Elastic-Job

XXL-JOB 和 Elastic-Job 是轻量级的分布式任务调度平台,支持动态任务管理和高可用性。

示例代码(XXL-JOB)

依赖

<dependency>
    <groupId>com.xuxueligroupId>
    <artifactId>xxl-job-coreartifactId>
    <version>3.1.2version>
dependency>

任务类

import com.xxl.job.core.handler.annotation.XxlJob;
import org.springframework.stereotype.Component;

@Component
public class XxlJobExample {
    @XxlJob("myJobHandler")
    public void myJobHandler() {
        System.out.println("XXL-JOB 任务执行:" + System.currentTimeMillis());
    }
}

配置类(XXL-JOB)

import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class XxlJobConfig {
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses("http://127.0.0.1:8080/xxl-job-admin");
        xxlJobSpringExecutor.setAppname("xxl-job-executor-sample");
        xxlJobSpringExecutor.setIp("127.0.0.1");
        xxlJobSpringExecutor.setPort(9999);
        return xxlJobSpringExecutor;
    }
}

优点

  • 功能强大:支持动态任务管理、分布式任务调度、任务监控等功能。
  • 高可用性:支持任务的容错和恢复机制。
  • 易于集成:与 Spring Boot 集成简单,配置方便。

缺点

  • 学习成本:需要额外学习 XXL-JOB 或 Elastic-Job 的使用方式。
  • 依赖外部系统:需要部署额外的调度中心(如 XXL-JOB 的管理平台)。

总结

在 Spring Boot 中,有多种方式可以实现定时任务,每种方式都有其适用场景和优缺点:

实现方式 优点 缺点 适用场景
@Scheduled 注解 简单易用,与 Spring 集成度高 不支持分布式任务 单节点应用,简单任务调度
ScheduledExecutorService 灵活性高,适合复杂任务调度 不支持复杂的时间调度策略 需要自定义线程池的任务调度
Quartz 框架 功能强大,支持复杂调度策略和分布式任务 配置复杂,学习成本高 需要复杂调度策略的应用
SchedulingConfigurer 接口 支持动态任务配置 实现复杂,需要额外开发工作 动态任务配置需求
TaskScheduler 接口 动态调度,与 Spring 集成度高 功能有限,不支持复杂调度策略 动态调整任务执行频率的场景
Redis 分布式任务 支持分布式任务调度,实现简单 功能有限,不支持复杂调度策略 分布式任务调度,简单场景
XXL-JOB / Elastic-Job 功能强大,支持动态任务管理和高可用性 需要额外部署调度中心,学习成本高 分布式任务调度,复杂场景

选择合适的定时任务实现方式

选择哪种定时任务实现方式取决于你的具体需求:

  1. 简单任务调度:如果任务逻辑简单,且仅在单个节点上运行,推荐使用 @Scheduled 注解。
  2. 复杂任务调度:如果需要支持复杂的时间调度策略(如 Cron 表达式),或者需要分布式任务调度,可以考虑使用 Quartz 框架。
  3. 动态任务配置:如果任务配置需要动态加载(如从数据库中读取),可以使用 SchedulingConfigurer 接口。
  4. 分布式任务调度:如果需要在多个节点上运行任务,且任务调度需要高可用性,推荐使用 XXL-JOB 或 Elastic-Job。
  5. 轻量级任务调度:如果需要快速实现简单的分布式任务调度,可以考虑使用 Redis。

示例项目结构

以下是一个简单的 Spring Boot 项目结构,展示了如何集成 @Scheduled 和 Redis 分布式任务:

my-spring-boot-project/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   ├── com.example.demo/
│   │   │   │   ├── application/
│   │   │   │   │   ├── DemoApplication.java
│   │   │   │   ├── config/
│   │   │   │   │   ├── SchedulingConfig.java
│   │   │   │   │   ├── RedisConfig.java
│   │   │   │   ├── service/
│   │   │   │   │   ├── ScheduledTask.java
│   │   │   │   │   ├── RedisDistributedTask.java
│   │   │   │   ├── model/
│   │   │   │   ├── controller/
│   │   │   │   ├── repository/
│   │   │   │   ├── util/
│   │   │   ├── resources/
│   │   │   │   ├── application.properties
│   │   │   │   ├── static/
│   │   │   │   ├── templates/
│   │   ├── test/
│   │   │   ├── java/
│   │   │   │   ├── com.example.demo/
│   │   │   │   │   ├── DemoApplicationTests.java
│   │   │   ├── resources/
│   ├── pom.xml

示例代码完整版

DemoApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

SchedulingConfig.java

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@EnableScheduling
public class SchedulingConfig {
    // 启用定时任务支持
}

ScheduledTask.java

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

@Component
public class ScheduledTask {
    // 固定频率:每5秒执行一次任务
    @Scheduled(fixedRate = 5000)
    public void executeTask() {
        System.out.println("【@Scheduled 任务】执行时间:" + LocalDateTime.now());
    }

    // Cron 表达式:每天中午12点执行一次任务
    @Scheduled(cron = "0 0 12 * * ?")
    public void executeCronTask() {
        System.out.println("【Cron 任务】执行时间:" + LocalDateTime.now());
    }
}

RedisConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        // 设置 Key 的序列化器
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());

        // 设置 Value 的序列化器
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        template.afterPropertiesSet();
        return template;
    }
}

RedisDistributedTask.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class RedisDistributedTask {
    private final RedisTemplate<String, String> redisTemplate;

    @Autowired
    public RedisDistributedTask(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Scheduled(fixedRate = 5000)
    public void executeTask() {
        String lockKey = "distributedTaskLock";
        String lockValue = String.valueOf(System.currentTimeMillis());
        Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 5, TimeUnit.SECONDS);
        if (lock != null && lock) {
            System.out.println("分布式任务执行:" + new Date());
        }
    }
}

application.properties

# Redis 配置
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.password=

结语

通过本文的介绍,你应该对 Spring Boot 中的定时任务实现方式有了更全面的了解。无论是简单的单节点任务,还是复杂的分布式任务,Spring Boot 都提供了灵活的解决方案。希望这些示例代码能帮助你快速实现定时任务的需求。

如果你有任何问题或建议,欢迎在评论区留言。

如果对你有帮助,点赞、收藏、关注是我更新的动力!

你可能感兴趣的:(后端专栏,spring,boot,后端,java)