Spring - Asynchronous Execution using @Async [Updated: Dec 22, 2016, Created: Dec 21, 2016] |
|
||
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 @EnableAsyncAnnotate 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 qualifierIf we have multiple executors configured as beans; we can resolve conflict between them by using a qualifier with @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 valueThe 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 methodIf 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 AsyncConfigurerThe 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:
|
|
||
|
|||
|