In modern web development, it is imperative to handle concurrent requests efficiently. One way to achieve this is by using asynchronous programming techniques. In this article, we will explore how to leverage CompletableFuture
and DeferredResult
in a Spring Boot application.
CompletableFuture
is a powerful class introduced in Java 8 that provides the ability to perform asynchronous computations. It allows you to express chained computations and handle the results asynchronously. Let's see how we can use it in our Spring Boot application.
First, we need to define our asynchronous operation. This can be done by creating a method that returns a CompletableFuture
inside a service class. For example, let's say we have a REST endpoint that retrieves a user's details from a remote service. We can define the following method in our service class:
@Service
public class UserService {
public CompletableFuture<UserDetails> fetchUserDetails(String userId) {
return CompletableFuture.supplyAsync(() -> {
// Perform the remote service call
UserDetails userDetails = remoteService.getUserDetails(userId);
return userDetails;
});
}
}
Here, the CompletableFuture.supplyAsync()
method is used to execute our service call asynchronously. The result will be wrapped in a CompletableFuture
object.
Next, we need to expose this asynchronous operation in our REST controller. We can achieve this by injecting the UserService
and using the CompletableFuture
returned by fetchUserDetails()
method.
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users/{userId}")
public CompletableFuture<ResponseEntity<UserDetails>> getUserDetails(@PathVariable String userId) {
return userService.fetchUserDetails(userId)
.thenApply(ResponseEntity::ok)
.exceptionally(ex -> ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
}
}
Here, we utilize the thenApply()
method of CompletableFuture
to transform the result into a ResponseEntity
object. Additionally, we use exceptionally()
to handle any exceptions that might occur during the process.
DeferredResult
is another asynchronous programming technique provided by Spring framework that can be used to handle long-running tasks. Unlike CompletableFuture
, it has more advanced features like timeouts and cancellations. Let's see how to use it in our Spring Boot application.
Similar to our previous example, we define an asynchronous operation in our service class. Here, we will assume that we are performing a computationally expensive task:
@Service
public class ImageProcessingService {
public void processImageAsync(String imageUrl, DeferredResult<String> deferredResult) {
CompletableFuture.supplyAsync(() -> {
// Simulating expensive image processing
String result = expensiveImageProcessing(imageUrl);
return result;
}).whenComplete((result, throwable) -> {
if (throwable != null) {
deferredResult.setErrorResult("An error occurred");
} else {
deferredResult.setResult(result);
}
});
}
private String expensiveImageProcessing(String imageUrl) {
// Perform the image processing
return "Processed image URL: " + imageUrl;
}
}
Here, the DeferredResult
object is taken as a parameter and will be completed with the result or error, depending on the outcome of the image processing task.
We can then expose this asynchronous operation in our REST controller by injecting the ImageProcessingService
:
@RestController
public class ImageController {
@Autowired
private ImageProcessingService imageProcessingService;
@GetMapping("/process-image")
public DeferredResult<String> processImage(@RequestParam("imageUrl") String imageUrl) {
DeferredResult<String> deferredResult = new DeferredResult<>();
imageProcessingService.processImageAsync(imageUrl, deferredResult);
return deferredResult;
}
}
In this example, we create a DeferredResult
object and pass it to the processImageAsync()
method of the ImageProcessingService
. We then return this DeferredResult
object as the response.
Using CompletableFuture
and DeferredResult
can greatly improve the performance and responsiveness of your Spring Boot application. They allow you to handle concurrent requests efficiently and execute long-running tasks asynchronously. By leveraging these techniques, you can create robust and scalable web applications with ease.
noob to master © copyleft