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?
#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
Related
Consider the following scenario: A class called EventHandlerImpl implements an interface called AsyncEventHandler, with a single handle() method marked with the Spring #Async annotation. EventHandlerImpl also extends an abstract superclass called AbstractEventHandler. This abstract superclass implements the plain EventHandler interface which also contains a handle() method, providing an implementation of the method which in turn calls upon another abstract method designed to be implemented by child classes.
public interface EventHandler {
void handle();
}
public interface AsyncEventHandler {
#Async
void handle();
}
#Component
public class EventHandlerImpl extends AbstractEventHandler implements AsyncEventHandler {
#Override
public void wrappedMethod() {
longRunningMethodWhichMayThrowException();
System.out.println("Handled long-running task!");
}
}
public class AbstractEventHandler implements EventHandler {
protected abstract void wrappedMethod();
#Override
public void handle() {
try {
wrappedMethod();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
Then, the required bean is injected in a constructor:
#Autowired
public DependentComponent(AsyncEventHandler asyncEventHandler) {
this.asyncEventHandler = asyncEventHandler;
}
The expected behaviour for the injected bean is asynchronous execution of its handle method as per the AsyncEventHandler interface (this is the entire reason for the existence of this interface).
Example:
System.out.println("Before long-running task execution");
asyncEventHandler.handle();
System.out.println("After long-running task execution");
Expected output:
Before long-running task execution
After long-running task execution
Handled long-running task!
The behaviour seen in reality does not match this. The handle() method is not executed asynchronously by Spring. I've found that having the AsyncEventHandler interface declared as such:
#Async
public interface AsyncEventHandler {
void handle();
}
(annotation on the interface level rather than the method level) makes the bean behave as expected. Digging into the Spring source code responsible for attaching and executing advisors such as the async advisor confirms that Spring is able to find the #Async annotation on the interface, but is unable to do so when it exists on the interface method in this particular scenario.
This raises the question: How is Spring meant to handle searching for annotations when the "same" method signature exists in multiple places in the inheritance hierarchy (with potentially different annotations)?
Is the discrepancy between annotating the method vs the interface a defined behaviour for Spring?
It is a class which instance is connected to the external service and it is listening constantly of it.
#Component
public class Service extends PollingBot {
#Value("${token}")
private String token;
#Override
public void onUpdateReceived(Update update) {
if (update.hasMessage()) {
}
}
public void sendMessageToUser(String message) {
try {
execute(sendMessage);
} catch (ApiException e) {
}
}
}
You could see that there is a method called sendMessageToUser which send message. It could not be static because execute method not allow static context. This method could not be separeted to other class. /
So, I have to call this method from other class. However I don't want to create additional instance of Service class otherwise I have two instances which are listen for updates, but I want it is sole class instance doing so.
I have tried to run a Application Context and run method from it, but it was not worked.
So, my question is very simple. How could I run this class non-static(!) method from other class?
By default all spring managed beans are singleton. You need to use #Autowired to inject the bean into other and then you can call the methods of that bean.
#Autowired
private Service service;
public void sendMessage(String message){
service.sendMessageToUser(message);
}
You can use #Autowired annotation to call a method of a bean class(component) in Spring. Also, as mentioned by default beans are singleton in spring so you don't need to worry about creating a single instance explicitly every time.
Try to use the below code in the calling class:
#Autowired
private Service service;
public void sendText() {
service.sendMessage(message);
}
I wanted to make a method writing to DB as async using #Async annotation.
I marked the class with the annotation #EnableAsync:
#EnableAsync
public class FacialRecognitionAsyncImpl {
#Async
public void populateDataInPushQueue(int mediaId, int studentId) {
//myCode
}
}
while calling the populateDataInPushQueue method, the write operation should be executed in another thread and the flow should continue from the class I am calling this method. But this is not happening and the program execution is waiting for this method to complete.
The #Async annotation has few limitations - check whether those are respected:
it must be applied to public methods only
it cannot be called from the same class as defined
the return type must be either void or Future
The following can be found at the documentation of #EnableAsync:
Please note that proxy mode allows for the interception of calls through the proxy only; local calls within the same class cannot get intercepted that way.
Another fact is that the class annotated with #EnableAsync must be a #Configuration as well. Therefore start with an empty class:
#EnableAsync
#Configuration
public class AsyncConfiguration { }
In my opinion, you are missing #Configuration annotation and your async service is not component scanned. Here is an example code fragment that should do the trick:
#Configuration
#EnableAsync //should be placed together.
public class FacialRecognitionAsyncService {
#Async
public void populateDataInPushQueue(int mediaId, int studentId) {
//myCode
}
}
#Configuration
#EnableAsync
public class FacialServiceConfig {
// this will make your service to be scanned.
#Bean
public FacialRecognitionAsyncService createFacialRecognitionService() {
return new FacialRecognitionAsyncService();
}
}
Now the service bean that is invoking the async method. Notice that it has been dependency injected. This way spring AOP proxies will be invoked on each invocation fo the facialService. Spring uses AOP in the back scenes in order to implement #Async.
#Service
public class MyBusinessService {
#Autowire
FacialRecognitionAsyncService facialService;
public myBusinessMethod() {
facialService.populateDataInPushQueue()
}
Notice that FacialService is injected in MyService through dependency injection.
I was trying to update the table row data from outside the controller (Inside some threads) and getting 'NullPointerException' always.
Thread code:
public class S3Thread implements Runnable {
#Autowired
private IAutomationService automationService;
#Override
public void run() {
Automation config = new Automation("user1","success");
automationService.updateAutomation(config);
}
}
NullPointer exception thrown on below line:
automationService.updateAutomation(config);
Note: I was able to create/update from the controller class.Only in Thread.
Well, this is the classical Why is my Spring #Autowired field null case. You create the S3Thread instance by yourself, and thus, no beans are injected into it.
Considering you're trying to just do something in a separate thread, you can consider using #Async:
#Async
public void updateAutomationConfiguration() {
Automation config = new Automation("user1", "success");
automationService.updateAutomation(config);
}
Notes:
You have to add the #EnableAsync annotation to any configuration class (eg. your main class) to make this work.
Spring uses proxying by default, which means that you can't add this updateAutomationConfiguration() class to your controller itself. Direct calls to methods within the same bean bypass the proxied logic. The solution is to put this method in a separate bean which can be autowired and invoked from within the controller. I've provided more detailed answers about alternative solutions in this answer.
Spring also has a getting started guide for creating asynchronous methods.
Alternatively, there are also some ways to execute asynchronous calls within controllers, for example by using CompletableFuture within a controller:
#PutMapping("/automation/configuration")
public CompletableFuture<String> updateAutomationConfiguration() {
return CompletableFuture.supplyAsync(() -> {
Automation config = new Automation("user1", "success");
return automationService.updateAutomation(config);
});
}
Related: How to create a non-blocking #RestController webservice in Spring?
Spring does not scan your runnable as it is not annotated with #Component.Try annotating it with #Component/#Service.
Don't forget to set scope required scope though!
There are 2 potential solutions to your problem:
Either you need to make S3Thread class a service by annotating it with #Service or #Component and autowiring it on the calling class, or you can alternatively use the constructor for initializing your automationService, e.g. private IAutomationService automationService = new AutomationService();
Since your thread class is not managed by spring you will not be able to inject the spring managed beans in the S3Thread class.
In order to do that you need to create a class or factory which should be hooked into the spring life cycle.
Once you have the hold of that class you can get the appropriate bean and pass the reference onto/or used in the S3Thread class directly. Something like this
#Component
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext appContext)
{
ctx = appContext;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
}
public class S3Thread implements Runnable {
#Override
public void run() {
Automation config = new Automation("user1","success");
IAutomationService automationService=
ApplicationContextUtils.getApplicationContext().getBean(IAutomationService .class);
automationService.updateAutomation(config);
}
}
I have the following classes and interfaces(simplified to make things clear).
#Serivce
class Service1 {
#Cacheable("cacheName")
public Metadata preLoadMetadata() {
// load data from database
}
}
#Service
class Service2 implements Loader {
#Autowired #Lazy
private Service1 service1;
#Overrride
public CacheWrapper getCacheWrapper() {
return new CacheWrapper(() -> service1.preLoadMetadata());
}
}
interface Loader {
CacheWrapper getCacheWrapper();
}
class CacheWrapper {
private Callable callable;
public CacheWrapper(Callable callable) {
this.callable = callable;
}
public getCallable() {
return callable;
}
}
The Spring bean responsible for loading the cache at the time of deployment.
#Component
class LoadCache {
#Autowired
private List<Loader> allLoaders;
#PostConstruct
public void load() {
allLoaders.getCacheWrapper().getCallable().call();
}
}
preLoadMetadata() doesn't save the data in the cache but it does execute. After deployment is complete and I call the same method preLoadMetadata() again, then it saves the data in the cache.
Why does #Cacheable doesn't work at the time of deployment?
If I manually use put method of Cache to populate cache inside method annotated with #PostConstruct, I am able to do it successfully while deployment.
I am using Tomcat as server.
I am using Couchbase cache behind Spring cache abstraction.
If you want to preload your cache I suggest you use an ApplicationListener that will execute once your application has started:
#Component
public class CacheInitializer implements ApplicationListener<ContextRefreshedEvent>{
#Autowired
private List<Loader> allLoaders;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
allLoaders.getCacheWrapper().getCallable().call();
}
}
The root cause for not working of Cacheable annotation:
afterSingletonsInstantiated() method sets the initialized flag to true only when BeanFactory has instantiated all the beans. If the initialized flag is false, no caching operations are performed. Here, inside the post construct, when we start pre-loading caches, then it is highly probable that LoadCache is not the last bean to be instantiated. Hence, no caching operations are performed(while using caching annotations).
Hence, EventListener annotation responding to ContextRefreshEvent is the best thing to use in this case which ensures that the context has started and then only loading of caches take place.