I am using Spring #Async on two classes. Both are ultimately implementing an interface. I am creating two separate ThreadPoolTaskExecutor so each class has its own ThreadPool to work off of. However due to I think something with proxy and how Spring implements Async classes, I have to put the #Async annotation on the base interface. Because of this, both classes end up using the same ThreadPoolTaskExecutor.
Is it possible to tell Spring that for this Bean (in this case I am calling the classes that implement that interface a Service), use this ThreadPoolTaskExecutor.
By default when specifying #Async on a method, the executor that will be used is the one supplied to the 'annotation-driven' element as described here.
However, the value attribute of the #Async annotation can be used when needing to indicate that an executor other than the default should be used when executing a given method.
#Async("otherExecutor")
void doSomething(String s) {
// this will be executed asynchronously by "otherExecutor"
}
In this case, "otherExecutor" may be the name of any Executor bean in the Spring container, or may be the name of a qualifier associated with any Executor, e.g. as specified with the element or Spring’s #Qualifier annotation
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html
And probably you need to specify the otherExecutor bean in you app with the pool settings you wish.
#Bean
public TaskExecutor otherExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
return executor;
}
Related
I have an application that uses CompletableFuture to process data from a stream async. The demo showcasing my async implementation is as follows:
#Async
#Transactional(readOnly = true)
public void beginProcessing() {
try(Stream<String> st = myJpa.getMyStream()) { // use spring jpa to get a stream of data from db
CompletableFuture.allOf(st.map(i -> CompletableFuture.supplyAsync(() ->
myMethod(i)))
.toArray(CompletableFuture[]::new)).join();
}
}
#Async
private CompletableFuture<Void> myMethod(String i) {
// logic goes here
}
And it works fine. However, at the moment the CompletableFuture uses some default thread pool to do its job. I would like to use a custom defined taskExecutor instead. This can be achieved by supplying a name of the taskExecutor as a 2nd argument of supplyAsync(...) method. I have done it with ForkJoin thread pool before, so I am positive that it works.
Now, I want to make it work with taskExecutor, so I have added a new bean to my config class as below (as well as another one which I will use elsewhere):
#Configuration
public class MyConfigClass {
#Bean
public TaskExecutor myNewExecutor() {
RejectedExecutionHandler re = new ThreadPoolExecutor.CallerRunsPolicy();
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(0);
executor.setThreadNamePrefix("myNewExecutor-");
executor.setRejectedExecutionHandler(re);
return executor;
}
#Bean
public TaskExecutor someOtherExecutor() { // would be used elsewhere
RejectedExecutionHandler re = new ThreadPoolExecutor.CallerRunsPolicy();
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(0);
executor.setThreadNamePrefix("someOtherExecutor-");
executor.setRejectedExecutionHandler(re);
return executor;
}
}
And then I autowired TaskExecutor into my class and added the 2nd arg. So the code looks like below:
#Autowired
private TaskExecutor myNewExecutor;
#Async
#Transactional(readOnly = true)
public void beginProcessing() {
try(Stream<String> st = myJpa.getMyStream()) { // use spring jpa to get a stream of data from db
CompletableFuture.allOf(st.map(i -> CompletableFuture.supplyAsync(() ->
myMethod(i),myNewExecutor))
.toArray(CompletableFuture[]::new)).join();
}
}
#Async
private CompletableFuture<Void> myMethod(String i) {
// logic goes here
}
However, that does not work since we have multiple beans of type TaskExecutor, so I get the following exception
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type 'org.springframework.core.task.TaskExecutor' available: expected single matching bean but found 3: myNewExecutor, someOtherExecutor, taskScheduler
Ok, so I thought I would be able to solve it by using #Qualifier annotation. However, it didn't work for me - the exception is still being thrown (I used said annotation under the #Bean annotaiton in a config class and then under the #Autowired annotation in my class where logic is.).
I know that you can make a method use a custom thread pool by giving each bean a name and having the corresponding name being passed as an arg to #Async annotation. So, I guess I could rework my code to be a simple for loop that calls mytMethod(...) with method's async annotation being replaced with #Async("myNewExecutor"). That would probably work but I would like to preserve CompletableFuture if I can, so wonder if anyone can identify what am I missing that results in the error mentioned above?
I feel like I am missing something trivial to make it work but I just cannot see it.
So ... I figured it out. It was indeed a trivial thing that I somehow missed (embarrassingly so).
By specifying which thread pool to use in my logic, I made sure to effectively tell #Async above myMethod(...) which of 3 available beans to use. But I forgot that I have one more #Async above beginProcessing() method. So naturally it was confused with these changes and was throwing the exception I mentioned. By specifying which thread pool it needs to use (using bean name trick I mentioned I know), the code works like a charm.
Maybe I have some outdated knowledge but it is the same as described here
https://stackoverflow.com/a/2657465/2674303
But now I noticed that this example works without any exceptions:
#Service
#EnableScheduling
public final class MyService {
#PostConstruct
public void init(){
System.out.println("MyService started");
}
#Scheduled(fixedDelay= 1000)
public void scheduleCall() {
System.out.println("scheduleCall");
}
}
Could you pease provide how does it work ?
#Scheduled annotation does not require proxy creation. The mechanism is different. After bean initialization Spring called post-processor ScheduledAnnotationBeanPostProcessor. Post processor searches for all methods annotated with #Scheduled and registers them to TaskScheduller for execution. Method execution will be performed via reflection.
See ScheduledAnnotationBeanPostProcessor source code.
#Scheduled
Processing of #Scheduled annotations is performed by registering a
ScheduledAnnotationBeanPostProcessor. This can be done manually or,
more conveniently, through the task:annotation-driven/ XML element
or #EnableScheduling annotation.
ScheduledAnnotationBeanPostProcessor
Bean post-processor that registers methods annotated with #Scheduled
to be invoked by a TaskScheduler according to the "fixedRate",
"fixedDelay", or "cron" expression provided via the annotation. This
post-processor is automatically registered by Spring's
task:annotation-driven XML element, and also by the
#EnableScheduling annotation.
Autodetects any SchedulingConfigurer instances in the container,
allowing for customization of the scheduler to be used or for
fine-grained control over task registration (e.g. registration of
Trigger tasks). See the #EnableScheduling javadocs for complete usage
details.
#PostConstruct also implemented via post-processor InitDestroyAnnotationBeanPostProcessor when dependency injection performed for bean, method which marked #PostConstruct will be executed thru reflection without proxy.
See InitDestroyAnnotationBeanPostProcessor source code
Summary:
In your example, Spring will create bean without proxy.
In case you will add a proxy-specific annotation, for example, #Transactional you will get an exception that proxy can not be created due to final class java.lang.IllegalArgumentException: Cannot subclass final class com.test.services.MyService
#Service
#EnableScheduling
public final class MyService {
#PostConstruct
public void init(){
System.out.println("MyService started");
}
#Scheduled(fixedDelay= 1000)
#Transactional
public void scheduleCall() {
System.out.println("scheduleCall");
}
}
But the current problem you also can solve to force use JDK dynamic proxy. We need to create an interface for class and set property spring.aop.proxy-target-class = false according to Proxying mechanisms
I thought it was always recommended to name your threads to make it easier to debug later on.
In my SpringBoot project I now used the #Async notation and later on a TaskExecutor, but could not find a way to name my threads.
Is there a way to do that, or not really done in the Spring abstractions?
You can use thread prefix conf property in task executor configuration, or you can use ThreadFactory if prefix is not enough
#Bean
public TaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("my_thread_prefix");
executor.setThreadFactory( new ThreadFactoryBuilder().setNameFormat("my-thread-%d").build())
executor.initialize();
return executor;
}
TaskExecutor in Spring is a functional interface that extends directly from Java's Executor. According to the documentation:
An object that executes submitted Runnable tasks. This interface
provides a way of decoupling task submission from the mechanics of how
each task will be run, including details of thread use, scheduling,
etc.
What this means is that it is not possible (and should not be required) to name your thread as you are not responsible for starting and managing it. That said, for debugging purposes, if you want to provide some name, you should do that to the thread pool itself by setting threadNamePrefix and/or threadGroupName properties.
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.
I'd like to create an executor service that I can use as follows:
#Asyn(value = "asyncService")
public void task() {
//...
}
When should the #Bean be created using ThreadPoolTaskExecutor or ThreadPoolExecutorFactoryBean?
#Bean
public ExecutorService getAsyncService() {
//when to favor ThreadPoolTaskExecutor over ThreadPoolExecutorFactoryBean
}
Are there any cases where one should be favored over the other?
Favor direct injection of the TaskExecutor unless running under an app server, mainframe, or other environment where you need special handling of threads. Like the docs say, it's easy to get confused on which class you're using otherwise.