Annotating a method of a bean with @Async will make it execute in a separate thread i.e. the caller will not wait for the completion of the called method.
Enable Async Support
The @EnableAsync annotation switches on Spring’s ability to run @Async methods in a background thread pool. For Enabling asynchronous processing with Java configuration got by simply adding the @EnableAsync to a configuration class:
@Configuration @EnableAsync public class SpringAsyncConfig { ... }
Asynchronous processing can also be enabled using XML configuration – by using the task namespace:
<task:executor id="myexecutor" pool-size="5" /> <task:annotation-driven executor="myexecutor"/>
Limitations for @Async
The reasons are simple – the method needs to be public so that it can be proxied. And self-invocation doesn’t work because it bypasses the proxy and calls the underlying method directly.
The @Async Annotation
Annotation that marks a method as a candidate for asynchronous execution. Can also be used at the type level, in which case all of the type’s methods are considered as asynchronous.
1. Methods with void Return Type
Following is the simple way to configure a method with void return type to run asynchronously:
@Async public void asyncMethodWithVoidReturnType() { System.out.println("Execute method asynchronously. " + Thread.currentThread().getName()); }
2. Methods With Return Type
@Async can also be applied to a method with return type – by wrapping the actual return in a Future:
@Service public class GitHubLookupService { RestTemplate restTemplate = new RestTemplate(); @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); } }
Spring also provides an AsyncResult class which implements Future. This can be used to track the result of the asynchronous method execution.
Now, let’s invoke the above method and retrieve the result of the asynchronous process using the Future object
public void testAsyncAnnotationForMethodsWithReturnType() throws InterruptedException, ExecutionException { System.out.println("Invoking an asynchronous method. " + Thread.currentThread().getName()); Future<User> future = GitHubLookupService.findUser(user); while (true) { if (future.isDone()) { System.out.println("Result from asynchronous process - " + future.get()); break; } System.out.println("Continue doing something else. "); Thread.sleep(1000); } }
The Executor
By default, Spring uses a SimpleAsyncTaskExecutor to actually run these methods asynchronously. The defaults can be overridden at two levels – at the application level or at the individual method level.
1. Override the Executor at the Method Level
@Configuration @EnableAsync public class SpringAsyncConfig { @Bean(name = "threadPoolTaskExecutor") public Executor threadPoolTaskExecutor() { return new ThreadPoolTaskExecutor(); } }
Then the executor name should be provided as an attribute in @Async:
@Async("threadPoolTaskExecutor") public void asyncMethodWithConfiguredExecutor() { System.out.println("Execute method with configured executor - " + Thread.currentThread().getName()); }
2. Override the Executor at the Application Level
@Configuration @EnableAsync public class SpringAsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { return new ThreadPoolTaskExecutor(); } }
The configuration class should implement the AsyncConfigurer interface – which will mean that it has the implement the getAsyncExecutor() method. It’s here that we will return the executor for the entire application – this now becomes the default executor to run methods annotated with @Async.
Exception Handling
We’ll create a custom async exception handler by implementing AsyncUncaughtExceptionHandler interface. The handleUncaughtException() method is invoked when there are any uncaught asynchronous exceptions:
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { @Override public void handleUncaughtException(Throwable throwable, Method method, Object... obj) { System.out.println("Exception message - " + throwable.getMessage()); System.out.println("Method name - " + method.getName()); for (Object param : obj) { System.out.println("Parameter value - " + param); } } }
When a method return type is a Future, exception handling is easy – Future.get() method will throw the exception.
But, if the return type is void, exceptions will not be propagated to the calling thread. Hence we need to add extra configurations to handle exceptions.
we also need to override the getAsyncUncaughtExceptionHandler() method to return our custom asynchronous exception handler:
@Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new CustomAsyncExceptionHandler(); }
Summary:
Congratulations! You’ve learned an asynchronous service. And also we have seen how to enable async call in spring framework.
Happy Spring Learning.
Strategy Design Patterns We can easily create a strategy design pattern using lambda. To implement…
Decorator Pattern A decorator pattern allows a user to add new functionality to an existing…
Delegating pattern In software engineering, the delegation pattern is an object-oriented design pattern that allows…
Technology has emerged a lot in the last decade, and now we have artificial intelligence;…
Managing a database is becoming increasingly complex now due to the vast amount of data…
Overview In this article, we will explore Spring Scheduler how we could use it by…