🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.
▶️ Subscribe to My YouTube Channel (178K+ subscribers): Java Guides on YouTube
▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube
🚀 Introduction: What is @Async in Spring Boot?
The @Async annotation in Spring Boot allows asynchronous execution of methods in a separate thread, improving performance for long-running operations without blocking the main thread.
✅ Key Features of @Async:
✔ Runs methods asynchronously without blocking execution.
✔ Uses Spring’s TaskExecutor to manage threads.
✔ Works with CompletableFuture<T>, Future<T>, and void return types.
✔ Can be used for background tasks, I/O operations, and API calls.
📌 In this guide, you’ll learn:
✅ How to enable and use @Async in Spring Boot.
✅ How to return values asynchronously.
✅ How to handle exceptions in async methods.
1️⃣ Enabling Async Execution in Spring Boot
Before using @Async, enable asynchronous execution in your Spring Boot application.
📌 Enable @Async in @Configuration Class
@Configuration
@EnableAsync // Enables async execution
public class AsyncConfig {
}
✅ @EnableAsync allows Spring to process @Async methods asynchronously.
2️⃣ Basic Example: Running a Method Asynchronously
📌 Example: Running an Async Method in a Service
1. Create an Async Service (NotificationService.java)
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class NotificationService {
@Async
public void sendEmail(String email) {
System.out.println("Sending email to: " + email);
try {
Thread.sleep(3000); // Simulating delay
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Email sent to: " + email);
}
}
2. Call Async Method from Controller (UserController.java)
@RestController
@RequestMapping("/api/users")
public class UserController {
private final NotificationService notificationService;
public UserController(NotificationService notificationService) {
this.notificationService = notificationService;
}
@PostMapping("/register")
public String registerUser(@RequestParam String email) {
notificationService.sendEmail(email); // Async method call
return "User registered successfully. Email will be sent asynchronously.";
}
}
📌 POST Request (POST /api/users/register?email=test@example.com)
📌 Console Output:
User registered successfully. Email will be sent asynchronously.
Sending email to: test@example.com
(After 3 seconds delay)
Email sent to: test@example.com
✅ The API responds immediately while email sending happens in the background.
3️⃣ Using CompletableFuture<T> to Return Async Results
To return asynchronous results, use CompletableFuture<T>.
📌 Example: Fetching Data Asynchronously
1. Async Service (DataService.java)
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class DataService {
@Async
public CompletableFuture<String> fetchData() {
try {
Thread.sleep(2000); // Simulate delay
} catch (InterruptedException e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture("Fetched Data Successfully!");
}
}
2. Controller (DataController.java)
@RestController
@RequestMapping("/api/data")
public class DataController {
private final DataService dataService;
public DataController(DataService dataService) {
this.dataService = dataService;
}
@GetMapping
public CompletableFuture<String> getData() {
return dataService.fetchData(); // Async execution
}
}
📌 GET Request (GET /api/data)
📌 Response (after 2 seconds delay):
Fetched Data Successfully!
✅ The response is wrapped inside CompletableFuture<T> and executed asynchronously.
4️⃣ Handling Exceptions in Async Methods
By default, exceptions in @Async methods are lost if the return type is void. To handle exceptions properly, return a CompletableFuture<T> or log the error.
📌 Example: Handling Exceptions in Async Methods
1. Async Service (TaskService.java)
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class TaskService {
@Async
public CompletableFuture<String> processTask() {
try {
Thread.sleep(2000);
throw new RuntimeException("Something went wrong!");
} catch (Exception e) {
return CompletableFuture.failedFuture(e);
}
}
}
2. Controller (TaskController.java)
@RestController
@RequestMapping("/api/tasks")
public class TaskController {
private final TaskService taskService;
public TaskController(TaskService taskService) {
this.taskService = taskService;
}
@GetMapping("/run")
public CompletableFuture<String> runTask() {
return taskService.processTask()
.exceptionally(ex -> "Error: " + ex.getMessage());
}
}
📌 GET Request (GET /api/tasks/run)
📌 Response (HTTP 200 OK):
Error: Something went wrong!
✅ Using exceptionally() ensures that errors are handled properly.
5️⃣ Configuring Thread Pool for Async Execution
By default, Spring Boot uses a single-threaded executor for @Async methods. You can define a custom thread pool for better performance.
📌 Example: Configuring a Thread Pool for @Async Tasks
1. Define a Custom Thread Pool (AsyncConfig.java)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("AsyncThread-");
executor.initialize();
return executor;
}
}
2. Use the Custom Executor in an Async Service
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class ReportService {
@Async("asyncExecutor")
public CompletableFuture<String> generateReport() {
return CompletableFuture.completedFuture("Report Generated Successfully!");
}
}
✅ Now, Spring Boot will use the configured thread pool instead of the default one.
🎯 Summary: Best Practices for Using @Async
✅ Always enable async execution using @EnableAsync.
✅ Use CompletableFuture<T> instead of void for better error handling.
✅ Configure a custom thread pool for scalability (ThreadPoolTaskExecutor).
✅ Log exceptions in async methods (exceptionally() for error handling).
✅ Use meaningful method names (fetchData(), sendEmail(), processTask()).
🚀 Following these best practices ensures efficient, non-blocking execution in Spring Boot!
Comments
Post a Comment
Leave Comment