原文地址:http://www.baeldung.com/spring-asyncjava
In this article we’ll explore the asynchronous execution support in Spring – and the @Async annotation.spring
Simply put – 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.app
Let’s start by enabling asynchronous processing with Java configuration – by simply adding the @EnableAsync to a configuration class:async
1
2
3
|
@Configuration
@EnableAsync
public
class
SpringAsyncConfig { ... }
|
The enable annotation is enough, but as you’d expect, there are also a few simple options for configuration as well:ide
Asynchronous processing can also be enabled using XML configuration – by using the task namespace:ui
1
2
|
<
task:executor
id
=
"myexecutor"
pool-size
=
"5"
/>
<
task:annotation-driven
executor
=
"myexecutor"
/>
|
First – let’s go over the rules – @Async has two limitations:this
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.spa
Following is the simple way to configure a method with void return type to run asynchronously:code
1
2
3
4
5
|
@Async
public
void
asyncMethodWithVoidReturnType() {
System.out.println(
"Execute method asynchronously. "
+ Thread.currentThread().getName());
}
|
@Async can also be applied to a method with return type – by wrapping the actual return in a Future:xml
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Async
public
Future<String> asyncMethodWithReturnType() {
System.out.println(
"Execute method asynchronously - "
+ Thread.currentThread().getName());
try
{
Thread.sleep(
5000
);
return
new
AsyncResult<String>(
"hello world !!!!"
);
}
catch
(InterruptedException e) {
//
}
return
null
;
}
|
Spring also provides a AsyncResult class which implements Future. This can be used to track the result of asynchronous method execution.
Now, let’s invoke the above method and retrieve the result of the asynchronous process using the Future object.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
void
testAsyncAnnotationForMethodsWithReturnType()
throws
InterruptedException, ExecutionException {
System.out.println(
"Invoking an asynchronous method. "
+ Thread.currentThread().getName());
Future<String> future = asyncAnnotationExample.asyncMethodWithReturnType();
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
);
}
}
|
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.
The required executor needs to be declared in a configuration class:
1
2
3
4
5
6
7
8
9
|
@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:
1
2
3
4
5
|
@Async
(
"threadPoolTaskExecutor"
)
public
void
asyncMethodWithConfiguredExecutor() {
System.out.println(
"Execute method with configured executor - "
+ Thread.currentThread().getName());
}
|
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:
1
2
3
4
5
6
7
8
9
10
|
@Configuration
@EnableAsync
public
class
SpringAsyncConfig
implements
AsyncConfigurer {
@Override
public
Executor getAsyncExecutor() {
return
new
ThreadPoolTaskExecutor();
}
}
|
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’ll create a custom async exception handler by implementingAsyncUncaughtExceptionHandler interface. The handleUncaughtException()method is invoked when there are any uncaught asynchronous exceptions:
1
2
3
4
5
6
7
8
9
10
11
12
|
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);
}
}
}
|
In the previous section we looked at the AsyncConfigurer interface implemented by the configuration class. As part of that, we also need to override thegetAsyncUncaughtExceptionHandler() method to return our custom asynchronous exception handler:
1
2
3
4
|
@Override
public
AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return
new
CustomAsyncExceptionHandler();
}
|
In this tutorial we looked at running asynchronous code with Spring. We started with the very basic configuration and annotation to make it work but also looked at more advanced configs such as providing our own executor, or exception handling strategies.