一、定时任务
在 Spring Boot 中,实现定时任务非常简单,主要通过 @Scheduled 注解和 TaskScheduler 接口来实现。以下是实现定时任务的详细步骤和方法:
- 启用定时任务支持
- 在 Spring Boot 应用中,首先需要启用定时任务支持。可以通过在配置类或主应用类上添加 @EnableScheduling 注解来实现。
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling // 启用定时任务支持 public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }
- 使用 @Scheduled 注解创建定时任务
- @Scheduled 注解用于标记一个方法为定时任务。可以通过 cron 表达式、固定延迟(fixedDelay)、固定速率(fixedRate)等方式配置任务的执行时间。
- Cron 表达式
- Cron 表达式:是一种灵活的时间配置方式,支持秒、分、时、日、月、周等字段。
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class MyScheduledTasks { @Scheduled(cron = "0 * * * * ?") // 每分钟执行一次 public void taskWithCron() { System.out.println("Cron 任务执行: " + System.currentTimeMillis()); } }
- 固定延迟(fixedDelay)
- fixedDelay:表示任务执行完成后,延迟指定时间再执行下一次任务。
@Scheduled(fixedDelay = 5000) // 每次任务完成后,延迟 5 秒再执行下一次 public void taskWithFixedDelay() { System.out.println("FixedDelay 任务执行: " + System.currentTimeMillis()); }
- 固定速率(fixedRate)
- fixedRate:表示任务以固定的速率执行,无论上一次任务是否完成。
@Scheduled(fixedRate = 3000) // 每 3 秒执行一次 public void taskWithFixedRate() { System.out.println("FixedRate 任务执行: " + System.currentTimeMillis()); }
- Cron 表达式
- @Scheduled 注解用于标记一个方法为定时任务。可以通过 cron 表达式、固定延迟(fixedDelay)、固定速率(fixedRate)等方式配置任务的执行时间。
- 配置定时任务线程池
- 创建自定义线程池
- Spring Boot 会自动将 @Scheduled 注解的任务绑定到 ThreadPoolTaskScheduler 自定义线程池中。
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 scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(10); // 设置线程池大小 scheduler.setThreadNamePrefix("MyScheduler-"); // 设置线程名前缀 return scheduler; } }
- 创建自定义线程池
二、异步任务
在 Spring Boot 中,实现异步任务非常简单,主要通过 @Async 注解和 TaskExecutor 接口来实现。异步任务适用于需要并发执行的场景,例如发送邮件、处理文件、调用外部 API 等。
-
启用异步任务支持
- 在 Spring Boot 应用中,首先需要启用异步任务支持。可以通过在配置类或主应用类上添加 @EnableAsync 注解来实现。
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync // 启用异步任务支持 public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }
-
使用 @Async 注解创建异步任务
- @Async 注解用于标记一个方法为异步任务。被标记的方法会在独立的线程中执行。
import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class MyAsyncService { @Async // 标记为异步方法 public void asyncTask() { System.out.println("异步任务开始执行,线程: " + Thread.currentThread().getName()); try { Thread.sleep(5000); // 模拟耗时操作 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("异步任务执行完成,线程: " + Thread.currentThread().getName()); } }
-
配置线程池
- 默认情况下,Spring Boot 使用 SimpleAsyncTaskExecutor 执行异步任务,每次都会创建一个新线程。为了更高效地管理线程,可以配置自定义线程池。
- 使用 ThreadPoolTaskExecutor 配置线程池
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; @Configuration public class AsyncConfig { @Bean(name = "taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); // 核心线程数 executor.setMaxPoolSize(20); // 最大线程数 executor.setQueueCapacity(50); // 队列容量 executor.setThreadNamePrefix("Async-"); // 线程名前缀 executor.initialize(); return executor; } }
- 在 @Async 注解中指定线程池的名称
@Async("taskExecutor") // 指定线程池 public void asyncTask() { System.out.println("异步任务执行,线程: " + Thread.currentThread().getName()); }
- 使用 ThreadPoolTaskExecutor 配置线程池
- 默认情况下,Spring Boot 使用 SimpleAsyncTaskExecutor 执行异步任务,每次都会创建一个新线程。为了更高效地管理线程,可以配置自定义线程池。
-
处理异步任务的返回值
- 如果异步方法有返回值,可以使用 Future 或 CompletableFuture 来获取结果。
- 使用 Future,调用方可以通过 Future.get() 方法获取结果
import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.concurrent.Future; @Service public class MyAsyncService { @Async public Future<String> asyncTaskWithReturn() { System.out.println("异步任务开始执行,线程: " + Thread.currentThread().getName()); try { Thread.sleep(5000); // 模拟耗时操作 } catch (InterruptedException e) { e.printStackTrace(); } return new AsyncResult<>("任务完成"); } } // 调用 MyAsyncService 的异步方法 Future<String> future = myAsyncService.asyncTaskWithReturn(); // 阻塞等待结果 String result = future.get();
- 使用 CompletableFuture,Java 8 引入的增强版 Future,支持更灵活的任务编排。
import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.concurrent.CompletableFuture; @Service public class MyAsyncService { @Async public CompletableFuture<String> asyncTaskWithCompletableFuture() { System.out.println("异步任务开始执行,线程: " + Thread.currentThread().getName()); try { Thread.sleep(5000); // 模拟耗时操作 } catch (InterruptedException e) { e.printStackTrace(); } return CompletableFuture.completedFuture("任务完成"); } } CompletableFuture<String> future = myAsyncService.asyncTaskWithCompletableFuture(); future.thenAccept(result -> System.out.println("任务结果: " + result));
- 使用 Future,调用方可以通过 Future.get() 方法获取结果
- 如果异步方法有返回值,可以使用 Future 或 CompletableFuture 来获取结果。
-
异常处理
- 异步任务中的异常不会传播到调用方,需要通过以下方式处理:
- 自定义异常处理器,实现 AsyncUncaughtExceptionHandler 接口,处理未捕获的异常。
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import java.lang.reflect.Method; import java.util.concurrent.Executor; @Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(20); executor.setQueueCapacity(50); executor.setThreadNamePrefix("Async-"); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (ex, method, params) -> { System.err.println("异步任务异常: " + ex.getMessage()); System.err.println("方法: " + method.getName()); }; } }
- 在异步方法内部捕获并处理异常。
@Async public void asyncTaskWithException() { try { // 业务逻辑 } catch (Exception e) { System.err.println("捕获异常: " + e.getMessage()); } }
- 自定义异常处理器,实现 AsyncUncaughtExceptionHandler 接口,处理未捕获的异常。
- 异步任务中的异常不会传播到调用方,需要通过以下方式处理: