Spring Async and Java’s 8 CompletableFuture
It is known that I am not the biggest fan of Spring, but at the time being I work for an organization that maintains too many projects utilizing Spring (in different forms and versions). I still remain skeptic towards Spring, of course there are some very nice ideas, there are some nice (too many) abstractions, there are some very handy ‘shortcuts’ to bootstrap complex projects. I am not going to elaborate on the things I don’t like in this post.
One thing I like on Spring’s documentation, is their getting started guides. Well written and concrete. I was reading through, a short guide, for ‘ Async‘ method execution, through SpringBoot /RestApi [ link] .
So this is this the implementation of the example ‘asynchronous’ findUser() method. Full source here.
@Async
public Future<User> findUser(String user) throws InterruptedException {
System.out.println("Looking up " + user);
User results = restTemplate.getForObject("https://api.github.com/users/" + user, User.class);
// Artificial delay of 1s for demonstration purposes
Thread.sleep(1000L);
return new AsyncResult<User>(results);
}I was wondering why there is still a ‘ Future’ in the example, while we have been introduced Java8, CompletableFuture. I guess the original authors want to preserve backwards compatibility with previous versions of Java (6 / 7 ) – where this construct is not available.
It seems that someone else had the same question, and wrote a very nice example here. In one of the comments, you can see a hint that from version 4.2 and onward the Spring API, would be compatible with the use of CompletableFuture, on top of Future & AsyncResult which are already provided. I thought, ` well it’s a shame, why not try it or even document it, because if someone lands on this example, he/she might stay with the current implementation` – why not use something standard?.
So I decided to make a tiny change, remove Future and replace it with CompletableFuture, also comment out the calls to Future.isDone() and replace it with the very handy CompletableFuture.allof() method.
So I changed the return type on the ‘service’ method while, updating the, caller code – to sync on all 3 futures and once allof() them were done, we could print the results.
package hello;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class GitHubLookupService {
RestTemplate restTemplate = new RestTemplate();
@Async
public CompletableFuture findUser(String user) throws InterruptedException {
System.out.println("Looking up " + user);
User results = restTemplate.getForObject("https://api.github.com/users/" + user, User.class);
// Artificial delay of 1s for demonstration purposes
Thread.sleep(1000L);
return CompletableFuture.completedFuture(results);
}
}
The modified, example can be found here. I found this and this blog posts from Tomasz Nirkewicz, a very nice and pragmatic walk through of CompletableFuture, rich method list. There is also a quite complete presentation by my favorite Devoxx Speaker , Jose Paumard you can find it here.
@Override
public void run(String... args) throws Exception {
// Start the clock
long start = System.currentTimeMillis();
// Kick of multiple, asynchronous lookups
CompletableFuture page1 = gitHubLookupService.findUser("PivotalSoftware");
CompletableFuture page2 = gitHubLookupService.findUser("CloudFoundry");
CompletableFuture page3 = gitHubLookupService.findUser("Spring-Projects");
// Wait until they are all done
//while (!(page1.isDone() && page2.isDone() && page3.isDone())) {
// Thread.sleep(10); //10-millisecond pause between each check
//}
//wait until all they are completed.
CompletableFuture.allOf(page1,page2,page3).join();
//I could join as well if interested.
// Print results, including elapsed time
System.out.println("Elapsed time: " + (System.currentTimeMillis() - start) +" ms");
System.out.println(page1.get());
System.out.println(page2.get());
System.out.println(page3.get());
}
Links
- https://spring.io/guides/gs/async-method/
- http://geowarin.github.io/completable-futures-with-spring-async.html
- http://www.nurkiewicz.com/2013/05/java-8-completablefuture-in-action.html
- http://www.nurkiewicz.com/2013/05/java-8-definitive-guide-to.html
- https://github.com/javapapo/projects-from-blog/tree/master/spring-async-complfuture
| Reference: | Spring Async and Java’s 8 CompletableFuture from our JCG partner Paris Apostolopoulos at the Papo’s log blog. |




