I am having troubles invoking a method asynchronously in Spring, when the invoker is an embedded library receiving notifications from an external system. The code looks as below:
@Service
public class DefaultNotificationProcessor implements NotificationProcessor {
private NotificationClient client;
@Override
public void process(Notification notification) {
processAsync(notification);
}
@PostConstruct
public void startClient() {
client = new NotificationClient(this, clientPort);
client.start();
}
@PreDestroy
public void stopClient() {
client.stop();
}
@Async
private void processAsync(Notification notification) {
// Heavy processing
}
}
The NotificationClient
internally has a thread in which it receives notifications from another system. It accepts a NotificationProcessor
in its constructor which is basically the object that will do the actual processing of notifications.
In the above code, I have given the Spring bean as the processor and attempted to process the notification asynchronously by using @Async
annotation. However, it appears the notification is processed in the same thread as the one used by NotificationClient
. Effectively, @Async
is ignored.
What am I missing here?
Simply put, annotating a method of a bean with @Async will make it execute in a separate thread. In other words, the caller will not wait for the completion of the called method. One interesting aspect in Spring is that the event support in the framework also has support for async processing if necessary.
The @EnableAsync annotation switches on Spring's ability to run @Async methods in a background thread pool. This class also customizes the Executor by defining a new bean. Here, the method is named taskExecutor , since this is the specific method name for which Spring searches.
Here @EnableAsync is used for enabling asynchronous processing with Java Spring Boot Configuration and switches Spring's ability to run @Async methods. The @Async Methods run in the background thread pool without interruption other parallel processes.
Never use @Async on top of a private method. In runtime, it will not able to create a proxy and, therefore, not work.
@Async
(as well as @Transactional
and other similar annotations) will not work when the method is invoked via this
(on when @Async
is used for private methods*), as long as you do not use real AspectJ compiletime or runtime weaving.
*the private method thing is: when the method is private, then it must been invoked via this
- so this is more the consequence then the cause
So change your code:
@Service
public class DefaultNotificationProcessor implements NotificationProcessor {
@Resource
private DefaultNotificationProcessor selfReference;
@Override
public void process(Notification notification) {
selfReference.processAsync(notification);
}
//the method must not been private
//the method must been invoked via a bean reference
@Async
void processAsync(Notification notification) {
// Heavy processing
}
}
See also the answers for: Does Spring @Transactional attribute work on a private method? -- this is the same problem
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With