I am using Spring Boot 1.5.x, and in details, I am using the #Async annotation. My problem is that I have the following method in a repository.
#Repository
class Repository {
#Async
CompletableFuture<String> findSomething() {
/* Some code that returns something */
}
}
And then, I have the following method in a service, which calls the above repository.
#Service
class Service {
private Repository repository;
// ...
#Async
CompletableFuture<String> findSomething() {
return repository.findSomething()
}
}
My question is: should I place the #Async annotation also in the service.findSomething() method? Or should I place the annotation only in the service method?
I mean, Spring should schedule the execution of a method marked with #Async annotation in a dedicated thread. Is it correct?
Thanks in advance.
Annotating a method with #Async will cause the caller to return immediately and the actual execution will occur in a separate thread as part of a task submitted to the default SimpleAsyncTaskExecutor (if you haven't configured another one). Please see the relevant spring documentation.
That being said, for your goal there's no added benefit in nesting the #Async. If your goal is to make Repository.findSomething asynchronous and to be able to call it from different places, not only Service.findSomething, you should annotate only this method.
Also, Service.findSomething is asynchronous itself, even if not annotated with #Async, in the scenario you depicted. The method is not blocking by calling CompletableFuture.get() and it will return immediately, although it will not be executed in a separate thread.
Related
I have a BaseRestController class that Rest controllers extend. It has a method that I want to run asynchronously.
public abstract class BaseRestController {
...
#Async("someThreadPoolTaskExecutor")
public void someAsyncTask() {
...
}
}
#RestController
public class MyRestController extends BaseRestController {
...
#GetMapping("/some/path")
public SomeEntity getSomething() {
...
this.someAsyncTask();
}
}
I have enabled Async using annotation, implemented a method that gets someThreadPoolTaskExecutor TaskExecutor and all. If I put #Async("someThreadPoolTaskExecutor") on a Service's (class annotated with #Service) method, it works but if I do so with someAsyncTask() in BaseRestController the code won't run asynchronously. Decorating the class with #Component didn't work either.
Spring guide on Async didn't help either. In it's demo, it also demonstrates Async with service class.
While, in the process, I realized that the behavior I wanted to implement was better off delegated to a service class, I am curious as to understand why the above won't work.
I'm using 2.1.0.RELEASE of Spring Boot.
There are couple of rules for #Async, you are doing the self-invocation which won't work here
it must be applied to public methods only
self-invocation – calling the async method from within the same class – won’t work
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.
I'm using spring boot. I was new to spring and started a spring project. So I didn't know about pre defined repositories (JPA, CRUD) which can be easily implemented. In case, I wanted to save a bulk data, so I use for loop and save one by one, Its taking more time. So I tried to use #Async. But it doesn't also work, is my concept wrong?
#Async has two limitation
it must be applied to public methods only
self-invocation – calling the async method from within the same class won’t work
1) Controller
for(i=0;i < array.length();i++){
// Other codes
gaugeCategoryService.saveOrUpdate(getEditCategory);
}
2) Dao implementation
#Repository
public class GaugeCategoryDaoImpl implements GaugeCategoryDao {
// Other codings
#Async
#Override
public void saveOrUpdate(GaugeCategory GaugeCategory) {
sessionFactory.getCurrentSession().saveOrUpdate(GaugeCategory);
}
}
After removing #Async , it working normally. But with that annotation it doesn't work. Is there any alternative method for time consuming? Thanks in advance.
the #Async annotation creates a thread for every time you call that method. but you need to enable it in your class using this annotation #EnableAsync
You also need to configure the asyncExecutor Bean.
You can find more details here : https://spring.io/guides/gs/async-method/
In my opinion, there are several issues with your code:
You overwrite the saveOrUpdate() method without any need to do so. A simple call to "super()" should have been enough to make #Async work.
I guess that you somewhere (within your controller class?) declare a transactional context. That one usually applies to the current thread. By using #Async, you might leave this transaction context as (because of the async DAO execution), the main thread may already be finished when saveOrUpdate() is called. And even though I currently don't know it exactly, there is a good change that the declared transaction is only valid for the current thread.
One possble fix: create an additional component like AsyncGaugeCategoryService or so like this:
#Component
public class AsyncGaugeCategoryService {
private final GaugeCategoryDao gaugeCategoryDao;
#Autowired
public AsyncGaugeCategoryService(GaugeCategoryDao gaugeCategoryDao) {
this.gaugeCategoryDao = gaugeCategoryDao;
}
#Async
#Transactional
public void saveOrUpdate(GaugeCategory gaugeCategory) {
gaugeCategoryDao.saveOrUpdate(gaugeCategory);
}
}
Then inject the service instead of the DAO into your controller class. This way, you don't need to overwrite any methods, and you should have a valid transactional context within your async thread.
But be warned that your execution flow won't give you any hint if something goes wrong while storing into the database. You'll have to check the log files to detect any problems.
I have the need to cache some the results of some asynchronous computations. In detail, to overcome this issue, I am trying to use Spring 4.3 cache and asynchronous computation features.
As an example, let's take the following code:
#Service
class AsyncService {
#Async
#Cacheable("users")
CompletableFuture<User> findById(String usedId) {
// Some code that retrieves the user relative to id userId
return CompletableFuture.completedFuture(user);
}
}
Is it possible? I mean, will the caching abstraction of Spring handle correctly the objects of type CompletableFuture<User>? I know that Caffeine Cache has something like that, but I can't understand if Spring uses it if properly configured.
EDIT: I am not interested in the User object itself, but in the CompletableFuture that represents the computation.
The community asks me to do some experiments, so I made them. I found that the answer to my question is simple: #Cacheable and #Async do not work together if they are placed above the same method.
To be clear, I was not asking for a way to directly make the cache return the object owned by a CompletableFuture. This is impossible, and if it isn't so, it will break the contract of asynchronous computation of the CompletableFuture class.
As I said, the two annotations do not work together on the same method. If you think about it, it is obvious. Marking with #Async is also #Cacheable means to delegate the whole cache management to different asynchronous threads. If the computation of the value of the CompletableFuture will take a long time to complete, the value in the cache will be placed after that time by Spring Proxy.
Obviously, there is a workaround. The workaround uses the fact the CompletableFuture is a promise. Let's have a look at the code below.
#Component
public class CachedService {
/* Dependecies resolution code */
private final AsyncService service;
#Cacheable(cacheNames = "ints")
public CompletableFuture<Integer> randomIntUsingSpringAsync() throws InterruptedException {
final CompletableFuture<Integer> promise = new CompletableFuture<>();
// Letting an asynchronous method to complete the promise in the future
service.performTask(promise);
// Returning the promise immediately
return promise;
}
}
#Component
public class AsyncService {
#Async
void performTask(CompletableFuture<Integer> promise) throws InterruptedException {
Thread.sleep(2000);
// Completing the promise asynchronously
promise.complete(random.nextInt(1000));
}
}
The trick is to create an incomplete promise and return it immediately from the method marked with the #Cacheable annotation. The promise will be completed asynchronously by another bean that owns the method marked with the #Async annotation.
As a bonus, I also implemented a solution that does not use the Spring #Async annotation, but it uses the factory methods available in the CompletableFuture class directly.
#Cacheable(cacheNames = "ints1")
public CompletableFuture<Integer> randomIntNativelyAsync() throws
InterruptedException {
return CompletableFuture.supplyAsync(this::getAsyncInteger, executor);
}
private Integer getAsyncInteger() {
logger.info("Entering performTask");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return random.nextInt(1000);
}
Anyway, I shared the complete solution to my GitHub problem, spring-cacheable-async.
Finally, the above is a long description of what the Jira SPR-12967 refers to.
I hope it helps.
Cheers.
As per SPR-12967, ListenableFuture (CompletableFuture) are not supported.
Add #Async annotation on methods in one class and #Cacheable annotation at method level in a different class.
Then invoke #Async method from a service or any different layer.
It worked for me, both Redis cache and Async, which improved the performance drastically.
In theory, it would work as long as
the implementation of CacheManager behind the #Cacheable is not serializing the cached objects (like a cache backed by Hazelcast)
Since the CompletableFuture holds a state, which can be modified by calling e.g. the cancel() method, it's important that all the users of the API won't mess around with the cached object. Otherwise, there might be the risk that the cached object inside the Future could not be retrieved anymore, and a cache eviction would be necessary
It's worth to verify in which order the proxies behind the annotations are called. i.e. is the #Cacheable proxy called always before the #Async one? Or the other way around? Or it depends? For example, if the #Async is called before, it will fire a Callable inside a ForkJoinPool, just to then retrieve the other object from the cache.
I tried the below approach and it seems to work.
create a method with #Cachable which does the actual business logic
create a method with #Async which calls the above #Cachable method and returns a CompletableFuture
call the method with #Async in your main execution flow
Example:
public class Main {
public void cachedAsyncData() {
try {
asyncFetcher.getData().get();
} catch(Exception e){}
}
}
public class AsyncFetcher {
#Async
public CompletableFuture<String> getData() {
return CompletableFuture.completedFuture(cacheFetcher.getData());
}
}
public class CacheFetcher {
#Cacheable
public String getData() {
return "DATA";
}
}
Please add annotation #EnableAsync at #Component or #Serice class levele.
Exp:
#Service
#Slf4j
#EnableAsync //Add it to here
public class CachingServiceImpl implements CachingService {
Hope to help you!
Is that possible to execute methods annotated as #After (in class annotated as #Aspec) asynchronous? For example using annotation #Async or implements Runnable or Callable. And the same question for methods annotated as #PostPersist.
The real case is sending mail after registration. I don't want to execute method from some MailService in register method because this service will have too many dependencies and will be difficult to testing. But sending mail have to be executed asynchronous because this operation is very expensive.
Should not be a problem. Just start a new thread in the method "after" method.
AOP works as follows (by default):
When you register a bean that requires some aspects to be applied to, Spring creates a proxy around that bean and when you get the bean from the application context(by ctx.getBean(MyBean.class) or by Autowire is somewhere) you receive the proxy that has the real/target bean inside.
So, for example, when you tell Spring to execute a method after some target method this happens:
the application calls the method of the proxy
If something has to be executed before the target method(like configured #Before action) it's done.
call the real/target method
If something has to be executed after the target method(like configured #After action) it's done.
The next method in the call stack is executed
So basically with #After you just "insert" a method in the call stack. You can start a new thread from there, it will return the control immediately and the next thing after the target method will be executed.
If i want a method to repeat async, can i use #Scheduled and #Async together ?
#Async
#Scheduled(fixedDelay = x)
public void doSomethingEveryXMinuteAsync() {
// action
}
or is there another standard way to achive this ?
There is no need to use #Async. Just use fixedRate attribute of #Scheduled instead of fixedDelay. Spring will make another invocation on the method after the given time regardless of any call is already being processed.
UPDATE:
Apparently fixedRate attribute does not enforce a scheduled method to be called asynchronously and increasing pool size of scheduler task executor only enables asynchronous execution of independent #Scheduled methods. Even putting #Async on the method does not make it work as OP has asked.
ScheduledAnnotationBeanPostProcessor just creates a Runnable from the #Scheduled method and does not create any pointcut as #Async method processor would. ScheduledThreadPoolExecutor waits until Runnable#run() is finished and sets the next execution time using the start time and the fixed rate. So if the method call takes more time than the scheduled time, the next task is triggered right after the previous call is finished.
An easy solution would be extracting the actual method into another class as a #Async method and calling this method from the #Scheduled method.
Implement SchedulingConfigurer and override configureTasks method.
Define poolsize more than one, It will work as you are expecting.
You should configure implementing SchedulingConfigurer:
#Configuration
#EnableScheduling
public class ScheduledConfiguration implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar)
{
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(10);
threadPoolTaskScheduler.setThreadNamePrefix("your-scheduler-");
threadPoolTaskScheduler.initialize();
scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
}
With this code you achieve parallel execution of your method which is annotated with #Scheduled.
You may also set the property:
spring.task.scheduling.pool.size
#Async and #Scheduled method shouldn't be in a same class.
follow the manual and add AsyncConfigurerhttps://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/EnableAsync.html