28. 如何在Spring Boot中处理异步任务?`@Async`注解如何使用?
在Spring Boot中,处理异步任务可以通过@Async
注解来实现。@Async
注解允许你在单独的线程中异步执行方法,而不阻塞调用者的线程。这对于提高应用程序的性能和响应性非常有用,尤其是在处理耗时的任务时。
1. 启用异步支持
首先,你需要在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);
}
}
@EnableAsync
注解启用了Spring的异步功能,允许在应用程序中使用@Async
注解来定义异步方法。
2. 使用@Async
注解
2.1 定义异步方法
在需要异步执行的方法上添加@Async
注解。该方法将在调用时立即返回,而实际任务将在单独的线程中执行。
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
@Async
public void sendEmail(String email) {
System.out.println("Sending email to " + email + " on thread: " + Thread.currentThread().getName());
// 模拟耗时操作
try {
Thread.sleep(5000); // 休眠5秒,模拟邮件发送时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Email sent to " + email);
}
}
在这个示例中,sendEmail
方法被标记为@Async
,因此它将在一个新的线程中异步执行。
2.2 调用异步方法
当调用异步方法时,方法会立即返回,异步任务会在后台执行。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class EmailController {
@Autowired
private EmailService emailService;
@GetMapping("/send-email")
public String sendEmail(@RequestParam String email) {
emailService.sendEmail(email);
return "Email is being sent to " + email;
}
}
当访问/send-email?email=test@example.com
时,sendEmail
方法会立即返回,而实际的邮件发送操作将在后台执行。
3. 返回值处理
3.1 使用Future
或CompletableFuture
异步方法可以返回java.util.concurrent.Future
或java.util.concurrent.CompletableFuture
,以便在异步任务完成后获取结果。
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class UserService {
@Async
public CompletableFuture<User> findUserById(Long id) {
System.out.println("Finding user on thread: " + Thread.currentThread().getName());
// 模拟耗时操作
try {
Thread.sleep(3000); // 休眠3秒,模拟数据库查询
} catch (InterruptedException e) {
e.printStackTrace();
}
User user = new User(id, "John Doe");
return CompletableFuture.completedFuture(user);
}
}
在这个示例中,findUserById
方法返回一个CompletableFuture
,表示异步计算的结果。
3.2 等待异步结果
可以使用CompletableFuture
的get()
方法来阻塞并等待异步任务完成,然后获取结果。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/{id}")
public User getUserById(@PathVariable Long id) throws Exception {
CompletableFuture<User> userFuture = userService.findUserById(id);
return userFuture.get(); // 阻塞等待异步任务完成
}
}
在这个示例中,getUserById
方法会阻塞,直到异步任务完成并返回用户信息。
4. 自定义异步任务执行器
Spring Boot 默认使用 SimpleAsyncTaskExecutor
来执行异步任务,但你可以通过自定义 TaskExecutor
来控制线程池的行为。
4.1 自定义线程池
可以通过配置类自定义 TaskExecutor
,并将其作为 @Async
的默认执行器。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class AsyncConfig {
@Bean(name = "asyncExecutor")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.setThreadNamePrefix("AsyncThread-");
executor.initialize();
return executor;
}
}
4.2 使用自定义线程池
在 @Async
注解中指定自定义的执行器:
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class UserService {
@Async("asyncExecutor")
public CompletableFuture<User> findUserById(Long id) {
System.out.println("Finding user on thread: " + Thread.currentThread().getName());
// 模拟耗时操作
try {
Thread.sleep(3000); // 休眠3秒,模拟数据库查询
} catch (InterruptedException e) {
e.printStackTrace();
}
User user = new User(id, "John Doe");
return CompletableFuture.completedFuture(user);
}
}
在这个示例中,findUserById
方法将使用名为asyncExecutor
的自定义线程池来执行异步任务。
5. 异常处理
5.1 处理异步任务中的异常
如果异步方法抛出异常,Spring默认不会将异常传播给调用者。你可以通过重写AsyncUncaughtExceptionHandler
来处理未捕获的异常。
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import java.lang.reflect.Method;
@Configuration
public class AsyncExceptionConfig implements AsyncConfigurer {
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
System.out.println("Exception occurred in async method: " + method.getName());
throwable.printStackTrace();
}
};
}
}
5.2 使用CompletableFuture
处理异常
如果使用CompletableFuture
,可以通过exceptionally()
方法来处理异常。
@Async
public CompletableFuture<User> findUserById(Long id) {
return CompletableFuture.supplyAsync(() -> {
if (id == null) {
throw new IllegalArgumentException("User ID cannot be null");
}
return new User(id, "John Doe");
}).exceptionally(ex -> {
System.out.println("Exception occurred: " + ex.getMessage());
return null;
});
}
6. 总结
在Spring Boot中,@Async
注解提供了一个简单而强大的方式来处理异步任务。你可以通过在方法上添加@Async
注解,将方法的执行移到另一个线程,从而提高应用程序的响应性和并发性。此外,Spring Boot支持自定义线程池、异步任务的异常处理,以及返回CompletableFuture
等高级特性,使得异步编程更加灵活和可控。