spring boot处理定时任务

举报
林欣 发表于 2025/07/27 11:38:12 2025/07/27
【摘要】 Spring Boot 提供了多种方式来处理定时任务,以下是主要的实现方法: 1. 使用 @Scheduled 注解这是最简单的方式,适用于简单的定时任务。 基本配置启用定时任务:@SpringBootApplication@EnableScheduling // 启用定时任务public class MyApplication { public static void main(S...

Spring Boot 提供了多种方式来处理定时任务,以下是主要的实现方法:

1. 使用 @Scheduled 注解

这是最简单的方式,适用于简单的定时任务。

基本配置

  1. 启用定时任务:
@SpringBootApplication
@EnableScheduling  // 启用定时任务
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
  1. 创建定时任务:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledTasks {

    // 每5秒执行一次
    @Scheduled(fixedRate = 5000)
    public void taskWithFixedRate() {
        System.out.println("Fixed rate task - " + System.currentTimeMillis() / 1000);
    }

    // 固定延迟执行(上一次执行完成后5秒再执行)
    @Scheduled(fixedDelay = 5000)
    public void taskWithFixedDelay() throws InterruptedException {
        Thread.sleep(1000); // 模拟任务执行时间
        System.out.println("Fixed delay task - " + System.currentTimeMillis() / 1000);
    }

    // 使用Cron表达式(每分钟的第30秒执行)
    @Scheduled(cron = "30 * * * * ?")
    public void taskWithCronExpression() {
        System.out.println("Cron task - " + System.currentTimeMillis() / 1000);
    }
}

@Scheduled 参数说明

  • fixedRate:固定速率执行,从任务开始时间计算间隔
  • fixedDelay:固定延迟执行,从任务结束时间计算间隔
  • initialDelay:初始延迟(与fixedRate或fixedDelay配合使用)
  • cron:使用Cron表达式定义执行时间

2. 使用 TaskScheduler 接口

提供更灵活的编程方式:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.time.Instant;

@Component
public class DynamicScheduledTasks {

    @Autowired
    private TaskScheduler taskScheduler;

    @PostConstruct
    public void scheduleTasks() {
        // 动态添加定时任务
        taskScheduler.schedule(
            () -> System.out.println("Dynamic task - " + Instant.now()),
            new CronTrigger("0/10 * * * * ?") // 每10秒执行一次
        );
    }
}

3. 使用 Quartz 框架

对于更复杂的调度需求,可以集成 Quartz:

添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

配置 Quartz

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

@Configuration
public class QuartzConfig {

    @Bean
    public JobDetail sampleJobDetail() {
        return JobBuilder.newJob(SampleJob.class)
                .withIdentity("sampleJob")
                .storeDurably()
                .build();
    }

    @Bean
    public Trigger sampleJobTrigger() {
        SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(10)
                .repeatForever();

        return TriggerBuilder.newTrigger()
                .forJob(sampleJobDetail())
                .withIdentity("sampleTrigger")
                .withSchedule(scheduleBuilder)
                .build();
    }
}

创建 Job 类

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

public class SampleJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("Quartz job executed - " + System.currentTimeMillis() / 1000);
    }
}

4. 动态管理定时任务

如果需要运行时动态管理定时任务:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import java.util.concurrent.atomic.AtomicBoolean;

@Component
public class DynamicScheduledTask implements SchedulingConfigurer {

    @Autowired
    private SomeService someService;

    private final AtomicBoolean enabled = new AtomicBoolean(true);

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(
            // 任务内容
            () -> {
                if (enabled.get()) {
                    someService.doSomething();
                }
            },
            // 触发器
            triggerContext -> {
                String cron = enabled.get() ? "0/5 * * * * ?" : "0 0 0 1 1 ?"; // 禁用时设为无效表达式
                return new CronTrigger(cron).nextExecutionTime(triggerContext);
            }
        );
    }

    public void setEnabled(boolean enabled) {
        this.enabled.set(enabled);
    }
}

5. 分布式定时任务

在集群环境中,可以使用以下方案避免重复执行:

  1. ShedLock:轻量级分布式锁

    <dependency>
        <groupId>net.javacrumbs.shedlock</groupId>
        <artifactId>shedlock-spring</artifactId>
        <version>4.42.0</version>
    </dependency>
    <dependency>
        <groupId>net.javacrumbs.shedlock</groupId>
        <artifactId>shedlock-provider-jdbc-template</artifactId>
        <version>4.42.0</version>
    </dependency>
    
    @Scheduled(cron = "0 0/15 * * * *")
    @SchedulerLock(name = "myScheduledTask", lockAtMostFor = "14m", lockAtLeastFor = "14m")
    public void myScheduledTask() {
        // 任务内容
    }
    
  2. Quartz + JDBC:使用数据库存储调度信息

最佳实践

  1. 对于简单任务,优先使用 @Scheduled
  2. 需要动态调度时使用 TaskScheduler
  3. 复杂调度需求使用 Quartz
  4. 集群环境考虑分布式锁或Quartz集群模式
  5. 避免长时间运行的任务阻塞调度线程(使用 @Async 异步执行)
  6. 合理设置异常处理,避免任务因异常终止

常见问题解决

  1. 定时任务不执行

    • 检查是否添加了 @EnableScheduling
    • 确保定时任务类被Spring管理(有 @Component 等注解)
    • 检查是否有未捕获的异常
  2. 任务执行时间过长影响后续执行

    • 使用 @Async 异步执行
    • 考虑调整任务执行频率
    • 优化任务代码性能
  3. 集群环境下重复执行

    • 实现分布式锁
    • 使用Quartz集群模式
    • 或通过业务逻辑去重
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。