Spring @Async and transaction management
There are cases in which it is necessary to execute pieces of code asynchronous. An example is the sending of a (JMS) message from your system to another system. If it is not important for the database transaction whether or not the message has been sent successfully you can send the message asynchronous. The advantage is that the user does not have to wait in the front-end while the message is being send. Another example of possible asynchronous execution is the case where messages have a clear ordering. In order to try to prevent message A from being overtaken by message B you might want to schedule the sending of message B asynchronous with a delay.
Spring supports the asynchronous execution of methods on your
@Components by means of the
@Async annotation. When using this annotation on a method of your
@Component Spring will always execute this method asynchronous. The Spring framework will use AOP (Aspect Oriented Programming) on the Spring proxy of the
@Component to wrap calls to this method in a
Runnable and schedule this
Runnable on a task executor. This task executor has a thread pool and when a thread in the pool becomes available the
Runnable will be executed on this thread. The caller of the method annotated with
@Async does not wait untill the
Runnable has been executed but terminates after the
Runnable has been scheduled.
If the method to be executed asynchronously has a return value it should return a
Future in its signature. This
Future can be used by the caller to track the progress of the asynchronous task and to retrieve the result of the task once it has been completely executed. The
@Async method should return an
ASyncResult (which implements the
Future interface) containing the actual result.
@Async annotation is being used extra care should be taken with respect to transactions. In normal circumstances (without
@Async) a transaction gets propagated through the call hierarchy from one Spring
@Component to the other.
However, when a
@Component calls a method annotated with
@Async this does not happen. The call to the asynchronous method is being scheduled and executed at a later time by a task executor and is thus handled as a 'fresh' call, i.e. without a transactional context. If the
@Async method (or the
@Component in which it is declared) is not
@Transactional by itself Spring will not manage any needed transactions.
In case the method sends a message to an ESB (Enterprise Service Bus) using JMS (or another protocol) this can lead to problems, because you probably want to use the transaction manager declared in your Spring application context (typically a JTA transaction manager). In order to make Spring manage the transaction of the
@Async method either the
@Component or the method itself should declare the
@Transactional annotation, this way Spring will manage the transaction even if a method is being executed asynchronous.
A final reminder: both
@Async work with AOP an proxying. The methods to which these annotation are applied should be public, otherwise the annotations are not picked up by Spring. When JUnit testing methods annotated with
@Async in combination with transactions and automatic rollbacks extra care should be taken, this will be the topic of another blog post in the near future.