java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot的@Scheduled和@Schedules

SpringBoot的@Scheduled和@Schedules的用法及区别介绍

作者:qwert1037

文章详细解析了Spring框架中`@Scheduled`注解的用法,包括Cron表达式、fixedRate、fixedDelay、initialDelay等参数的使用方法及场景选择,还介绍了如何启用定时任务、自定义TaskScheduler、错误处理、最佳实践及常见问题解决方法,并通过案例展示了如何配置定时任务

@Scheduled 的详细解析

参数详解

cron:使用Cron表达式来指定复杂的调度模式。

Cron表达式的格式如下:

Cron表达式的每个字段可以是具体的值、范围、列表或通配符(*)。

例如:

fixedRate:指定以固定的速率重复执行任务,从前一次任务开始时刻算起。它不会等待前一个任务完成,因此如果任务执行时间超过了设定的时间间隔,可能会有重叠的任务实例在运行。

fixedDelay:类似于 fixedRate,但是它是以前一次任务的完成时刻作为下一次任务启动的时间基准。这种方式可以确保每次只有一个任务实例在运行,前提是任务的执行时间短于延迟时间。

initialDelay:在第一次执行之前等待的时间(毫秒)。这个参数通常与 fixedRatefixedDelay 一起使用,用来设置首次执行前的延迟。

zone:定义时区,默认是系统的默认时区。如果你的应用需要在全球不同地区运行,明确指定时区可能是很重要的。

Cron表达式、fixedRate、fixedDelay、initialDelay如何选择

示例代码:

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledTasks {

    // 每天中午12点执行(上海时区)
    @Scheduled(cron = "0 0 12 * * ?", zone = "Asia/Shanghai")
    public void scheduledTaskUsingCron() {
        System.out.println("Scheduled task using cron at Asia/Shanghai timezone.");
    }

    // 每5秒执行一次,首次执行前等待2秒
    @Scheduled(fixedRate = 5000, initialDelay = 2000)
    public void scheduledTaskWithFixedRate() {
        System.out.println("Scheduled task with fixed rate.");
    }

    // 上次任务完成后等待3秒再执行下一次
    @Scheduled(fixedDelay = 3000)
    public void scheduledTaskWithFixedDelay() {
        System.out.println("Scheduled task with fixed delay.");
    }
}

@Schedules 的详细解析

@Schedules 允许多个 @Scheduled 注解组合在一起,为同一个方法设定多种不同的调度策略。

这对于那些需要在多个不同时间点或条件下触发的方法非常有用。

示例代码:

import org.springframework.scheduling.annotation.Schedules;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class MultipleScheduledTasks {

    // 每天中午12点执行,并且每5秒也执行一次
    @Schedules({
        @Scheduled(cron = "0 0 12 * * ?"),
        @Scheduled(fixedRate = 5000)
    })
    public void multipleScheduledTasks() {
        System.out.println("Multiple scheduled tasks.");
    }
}

启用和管理定时任务

要使这些注解生效,你需要确保你的Spring应用已经启用了对它们的支持。

这可以通过在配置类上添加 @EnableScheduling 来实现:

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

@Configuration
@EnableScheduling
public class SchedulingConfig {
    // 配置类内容
}

自定义 TaskScheduler

对于更复杂的需求,比如调整线程池大小或者设置线程名称前缀等,你可以通过自定义 TaskScheduler 来进行配置。

Spring提供了几种内置的调度器实现,如 ThreadPoolTaskSchedulerConcurrentTaskScheduler

示例代码:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
public class SchedulerConfig {

    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10); // 设置线程池大小
        taskScheduler.setThreadNamePrefix("MyScheduledTask-");
        taskScheduler.setErrorHandler(t -> {
            System.err.println("Error occurred in scheduled task: " + t.getMessage());
        });
        taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        taskScheduler.setAwaitTerminationSeconds(60);
        return taskScheduler;
    }
}

错误处理

当一个预定任务抛出异常时,默认情况下Spring会记录错误日志,但任务本身不会被取消。

如果你想改变这种行为,可以使用 DelegatingErrorHandlingRunnable 或者直接在 ThreadPoolTaskScheduler 中设置错误处理器(如上面的示例所示)。

自定义错误处理逻辑

你可以创建自己的错误处理器来捕获并处理异常:

import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("Async-");
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler() {
            @Override
            public void handleUncaughtException(Throwable ex, Method method, Object... params) {
                // 自定义异常处理逻辑
                System.err.println("Exception in async task: " + ex.getMessage());
            }
        };
    }
}

最佳实践

处理定时任务中的常见问题

案例分析

假设你正在开发一个电子商务平台,需要每天凌晨2点生成前一天的销售报告。

你可以使用 @Scheduled 注解来安排这个任务:

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public class DailyReportService {

    @Scheduled(cron = "0 0 2 * * ?", zone = "Asia/Shanghai")
    public void generateDailySalesReport() {
        // 执行生成销售报告的逻辑
        System.out.println("Generating daily sales report at Asia/Shanghai timezone.");
    }
}

此外,你还可以结合上述的最佳实践来增强任务的可靠性,例如:

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文