Spring provides annotation support for asynchronous method execution via @Async and @EnableAsync.
We are going to explore different aspects of this feature with quick examples.
Simple use of @Async and @EnableAsync
Annotate the configuration class with @EnableAsync
@EnableAsync
@Configuration
public class MyConfig {
@Bean
public MyBean myBean () {
return new MyBean();
}
}
Note that we don't have to provide a TaskExecutor as a bean. Spring will use a default executor implicitly.
Use @Async on bean's methods:
public class MyBean {
@Async
public void runTask () {
System.out.printf("Running task thread: %s%n",
Thread.currentThread().getName());
}
}
Calling async method from main method:
public class AsyncExample {
public static void main (String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class);
MyBean bean = context.getBean(MyBean.class);
System.out.printf("calling async method from thread: %s%n",
Thread.currentThread().getName());
bean.runTask();
}
}
Output
calling async method from thread: main
Running task thread: SimpleAsyncTaskExecutor-1
Using Executor other than default:
To specify a custom Executor we just need to configure it as a bean:
@EnableAsync
@Configuration
public class MyConfig {
@Bean
public MyBean myBean () {
return new MyBean();
}
@Bean
public TaskExecutor taskExecutor () {
return new ConcurrentTaskExecutor(
Executors.newFixedThreadPool(3));
}
}
MyBean and main classes are same. This time we will have this output:
calling async method from thread: main
Running task thread: pool-1-thread-1
Using a qualifier
If we have multiple executors configured as beans; we can resolve conflict between them by using a qualifier with @Async("qualifierValue") :
@EnableAsync
@Configuration
public class MyConfig {
@Bean
public MyBean myBean () {
return new MyBean();
}
@Bean
@Qualifier("myExecutor1")
public TaskExecutor taskExecutor2 () {
return new ConcurrentTaskExecutor(
Executors.newFixedThreadPool(3));
}
@Bean
@Qualifier("myExecutor2")
public TaskExecutor taskExecutor () {
return new ThreadPoolTaskExecutor();
}
}
public class MyBean {
@Async("myExecutor2")
public void runTask () {
System.out.printf("Running task thread: %s%n",
Thread.currentThread().getName());
}
}
public class AsyncExecutorWithQualifierExample {
public static void main (String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class);
MyBean bean = context.getBean(MyBean.class);
System.out.printf("calling async method from thread: %s%n",
Thread.currentThread().getName());
bean.runTask();
ThreadPoolTaskExecutor exec = context.getBean(ThreadPoolTaskExecutor.class);
exec.getThreadPoolExecutor().shutdown();
}
}
Output
calling async method from thread: main
Running task thread: taskExecutor-1
Async method arguments and return value
The methods annotated with @Async can accept any method arguments. Using a return value will probably always return a null value if its type is other than Future.
@EnableAsync
@Configuration
public class MyConfig {
@Bean
public MyBean myBean () {
return new MyBean();
}
}
public class MyBean {
@Async
public String runTask (String message) {
System.out.printf("Running task thread: %s%n",
Thread.currentThread().getName());
System.out.printf("message: %s%n", message);
System.out.println("task ends");
return "return value";
}
}
public class AsyncArgAndReturnValueExample {
public static void main (String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(
MyConfig.class);
MyBean bean = context.getBean(MyBean.class);
System.out.printf("calling MyBean#runTask() thread: %s%n",
Thread.currentThread().getName());
String s = bean.runTask("from main");
System.out.println("call MyBean#runTask() returned");
System.out.println("returned value: " + s);
}
}
Output
calling MyBean#runTask() thread: main
call MyBean#runTask() returned
returned value: null
INFO: No TaskExecutor bean found for async processing
Running task thread: SimpleAsyncTaskExecutor-1
message: from main
task ends
Returning a Future instance from Async method
If Async methods return values, they should return an instance of java.util.concurrent.Future:
@EnableAsync
@Configuration
public class MyConfig {
@Bean
public MyBean myBean () {
return new MyBean();
}
}
public class MyBean {
@Async
public CompletableFuture<String> runTask () {
System.out.printf("Running task thread: %s%n",
Thread.currentThread().getName());
CompletableFuture<String> future = new CompletableFuture<String>() {
@Override
public String get () throws InterruptedException, ExecutionException {
return " task result";
}
};
return future;
}
}
public class AsyncReturningFutureExample {
public static void main (String[] args) throws Exception {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class);
MyBean bean = context.getBean(MyBean.class);
System.out.printf("calling MyBean#runTask() thread: %s%n",
Thread.currentThread().getName());
CompletableFuture<String> r = bean.runTask();
System.out.println("result from task:" + r.get());
}
Output
calling MyBean#runTask() thread: main
Running task thread: SimpleAsyncTaskExecutor-1
result from task: task result
Async method can also return Spring's ListenableFuture.
Using AsyncConfigurer
The interface AsyncConfigurer can be implemented by @Configuration classes to customize the Executor instance or handling exception thrown in async method by using AsyncUncaughtExceptionHandler:
@EnableAsync
@Configuration
public MyConfig implements AsyncConfigurer{
@Bean
public MyBean myBean () {
return new MyBean();
}
@Bean("taskExecutor")
@Override
public Executor getAsyncExecutor () {
return new ConcurrentTaskExecutor(
Executors.newFixedThreadPool(3));
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler () {
return (throwable, method, objects) ->
System.out.println("-- exception handler -- "+throwable);
}
}
public class MyBean {
@Async
public void runTask () {
System.out.printf("Running task thread: %s%n",
Thread.currentThread().getName());
throw new RuntimeException("test exception");
}
}
public class AsyncConfigurerExample {
public static void main (String[] args) throws ExecutionException, InterruptedException {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(
MyConfig.class);
MyBean bean = context.getBean(MyBean.class);
System.out.printf("calling MyBean#runTask() thread: %s%n",
Thread.currentThread().getName());
bean.runTask();
ConcurrentTaskExecutor exec =
(ConcurrentTaskExecutor) context.getBean("taskExecutor");
ExecutorService es = (ExecutorService) exec.getConcurrentExecutor();
es.shutdown();
}
}
Output
calling MyBean#runTask() thread: main
Running task thread: pool-1-thread-1
-- exception handler -- java.lang.RuntimeException: test exception
@Async on class level
@Async can also be used at the class level, in which case all of the class methods will be executed asynchronously.
Example ProjectDependencies and Technologies Used: - Spring Context 4.3.4.RELEASE: Spring Context.
- JDK 1.8
- Maven 3.3.9
|