Spring @Async Not Working

JavaSpringAsynchronous

Java Problem Overview


An @Async method in a @Service-annotated class is not being called asynchronously - it's blocking the thread.

I've got <task: annotation-driven /> in my config, and the call to the method is coming from outside of the class so the proxy should be being hit. When I step through the code, the proxy is indeed hit, but it doesn't seem to go anywhere near any classes related to running in a task executor.

I've put breakpoints in AsyncExecutionInterceptor and they never get hit. I've debugged into AsyncAnnotationBeanPostProcessor and can see advice getting applied.

The service is defined as an interface (with the method annotated @Async there for good measure) with the implementation's method annotated @Async too. Neither are marked @Transactional.

Any ideas what may have gone wrong?

-=UPDATE=-

Curiously, it works only when I have my task XML elements in my app-servlet.xml file, and not in my app-services.xml file, and if I do my component scanning over services from there too. Normally I have one XML file with only controllers in it (and restrict the component-scan accordingly), and another with services in it (again with a component-scan restricted such that it doesn't re-scan the controllers loaded in the other file).

app-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:jee="http://www.springframework.org/schema/jee" 
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:webflow="http://www.springframework.org/schema/webflow-config" 
xmlns:task="http://www.springframework.org/schema/task"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-3.0.xsd
	http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
	http://www.springframework.org/schema/task
	http://www.springframework.org/schema/task/spring-task-3.0.xsd
	http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
	http://www.springframework.org/schema/jee 
	http://www.springframework.org/schema/jee/spring-jee-3.0.xsd"
>
<task:annotation-driven executor="executor" />
<task:executor id="executor" pool-size="7"/>

<!-- Enable controller annotations -->
<context:component-scan base-package="com.package.store">
	<!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> -->
</context:component-scan>

<tx:annotation-driven/>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

<mvc:annotation-driven conversion-service="conversionService" />

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="prefix" value="/WEB-INF/jsp/" />
	<property name="suffix" value=".jsp" />
</bean>

app-services.xml (doesn't work when specified here)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-3.0.xsd
		http://www.springframework.org/schema/task
		http://www.springframework.org/schema/task/spring-task-3.0.xsd">

	<!-- Set up Spring to scan through various packages to find annotated classes -->
	<context:component-scan base-package="com.package.store">
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
	</context:component-scan>

	<task:annotation-driven executor="han" />
	<task:executor id="han" pool-size="6"/>
	...

Am I missing something glaringly obvious in my configuration, or is there some subtle interplay between config elements going on?

Java Solutions


Solution 1 - Java

For me the solution was to add @EnableAsync on my @Configuration annotated class:

@Configuration
@ComponentScan("bla.package")
@EnableAsync
public class BlaConfiguration {

}

Now the class in package bla.package which has @Async annotated methods can really have them called asynchronously.

Solution 2 - Java

With the help of this excellent answer by Ryan Stewart, I was able to figure this out (at least for my specific problem).

In short, the context loaded by the ContextLoaderListener (generally from applicationContext.xml) is the parent of the context loaded by the DispatcherServlet (generally from *-servlet.xml). If you have the bean with the @Async method declared/component-scanned in both contexts, the version from the child context (DispatcherServlet) will override the one in the parent context (ContextLoaderListener). I verified this by excluding that component from component scanning in the *-servlet.xml -- it now works as expected.

Solution 3 - Java

Jiří Vypědřík's answer solved my problem. Specifically,

> 2. Check if your method annotated with @Async is public.

Another useful information from Spring tutorials https://spring.io/guides/gs/async-method/:

> Creating a local instance of the FacebookLookupService class does NOT > allow the findPage method to run asynchronously. It must be created inside > a @Configuration class or picked up by @ComponentScan.

What this means is that if you had a static method Foo.bar(), calling it in that manner wouldn't execute it in async, even if it was annotated with @Async. You'll have to annotate Foo with @Component, and in the calling class get an @Autowired instance of Foo.

Ie, if you have a annotated method bar in class Foo:

@Component
class Foo { 
   @Async
   public static void bar(){ /* ... */ }

   @Async
   public void bar2(){ /* ... */ }
}

An in your caller class:

class Test {

  @Autowired Foo foo;

  public test(){
     Foo.bar(); // Not async
     foo.bar(); // Not async
     foo.bar2(); // Async
  }

}

Edit: Seems like calling it statically also doesn't execute it in async.

Hope this helps.

Solution 4 - Java

  1. Try adding proxy-target-class="true" to all <*:annotation-driven/> elements that support this attribute.
  2. Check if your method annotated with @Async is public.

Solution 5 - Java

In my case the @Async method was defined in same class as the sync method that used it, and that apparently caused all jobs to hang on current thread.

Bad

@Component
@EnableAsync
public class TranslationGapiReader {
	@Async
	public CompletableFuture<GapiFile> readFile(String fileId) {
		try { Thread.sleep(2000); } catch (Exception exc) { throw new RuntimeException("ololo", exc); }
		return CompletableFuture.completedFuture(null);
	}

	public Stream<GapiFile> readFiles(Iterable<String> fileIds) {
		List<CompletableFuture<GapiFile>> futures = new ArrayList<>();
		for (String fileId: fileIds) {
			futures.add(readFile(fileId));
		}
		return Stream.empty();
    }
}

Good

@Component
@EnableAsync
public class AsyncGapiFileReader {
	@Async
	public CompletableFuture<TranslationGapiReader.GapiFile> readFile(String fileId) {
		try { Thread.sleep(2000); } catch (Exception exc) { throw new RuntimeException("ololo", exc); }
		return CompletableFuture.completedFuture(null);
	}
}
@Component
@EnableAsync
public class TranslationGapiReader {
	@Autowired
	AsyncGapiFileReader asyncGapiFileReader;

	public Stream<GapiFile> readFiles(Iterable<String> fileIds) {
		List<CompletableFuture<GapiFile>> futures = new ArrayList<>();
		for (String fileId: fileIds) {
			futures.add(asyncGapiFileReader.readFile(fileId));
		}
		return Stream.empty();
    }
}

I'm not Spring guru enough to understand why does it only work when the @Async method is in a different class, but that's what fixes the issue from my observations.

Solution 6 - Java

Firstly make your .xml config looks like:

<task:scheduler id="myScheduler" pool-size="10" />
<task:executor id="myExecutor" pool-size="10" />
<task:annotation-driven executor="myExecutor" scheduler="myScheduler" proxy-target-class="true" />

(Yes, scheduler count and executor thread pool size is configurable)

Or just use default:

<!-- enable task annotation to support @Async, @Scheduled, ... -->
<task:annotation-driven />

Secondly make sure @Async methods are public.

Solution 7 - Java

@Async can not be used in conjunction with lifecycle callbacks such as @PostConstruct. To asynchonously initialize Spring beans you currently have to use a separate initializing Spring bean that invokes the @Async annotated method on the target then.

public class SampleBeanImpl implements SampleBean {

  @Async
  void doSomething() { … }
}


public class SampleBeanInititalizer {

  private final SampleBean bean;

  public SampleBeanInitializer(SampleBean bean) {
    this.bean = bean;
  }

  @PostConstruct
  public void initialize() {
    bean.doSomething();
  }
}

source

Solution 8 - Java

I realized following the tutorial async-method tutorial code that my issue source was: the bean with the annotated @Async method was not being created wrapped in a proxy. I started digging and realized that there was a message saying

> Bean 'NameOfTheBean' is not eligible for getting processed by all > BeanPostProcessors (for example: not eligible for auto-proxying)

You can see here responses about this issue and its basically that BeanPostProcessors are required by every Bean, so every bean injected here and its dependencies will be excluded to be processed later by other BeanPostProcessors, because it corrupted the life cycle of beans. So identify which is the BeanPostProcessor that is causing this and dont use or create beans inside of it.

In my case i had this configuration

@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {

    @Autowired
    private Wss4jSecurityInterceptor securityInterceptor;

    @Autowired
    private DefaultPayloadLoggingInterceptor payloadLoggingInterceptor;

    @Override
    public void addInterceptors(List<EndpointInterceptor> interceptors) {
        interceptors.add(securityInterceptor);
        interceptors.add(payloadLoggingInterceptor);
    }
}

WsConfigurerAdapter is actually a BeanPostProcessor and you realize it because there is always a pattern: @Configuration that extends classes and override some of it functions to install or tweak beans involved in some non functional features, like web service or security.

In the aforementioned example you have to override the addInterceptors and added interceptors beans, so if you are using some annotation like @Async inside DefaultPayloadLoggingInterceptor it wont work. What is the solution? Get ride of WsConfigurerAdapter to start. After digging a bit i realized a class named PayloadRootAnnotationMethodEndpointMapping at the end was which had all valid interceptors, so i did it manually insted of overriding a function.

@EnableWs
@Configuration
public class WebServiceConfig {

    @Autowired
    private Wss4jSecurityInterceptor securityInterceptor;

    @Autowired
    private DefaultPayloadLoggingInterceptor payloadLoggingInterceptor;

    @Autowired
    public void setupInterceptors(PayloadRootAnnotationMethodEndpointMapping endpointMapping) {
        EndpointInterceptor[] interceptors = {
                securityInterceptor,
                payloadLoggingInterceptor
        };

        endpointMapping.setInterceptors(interceptors);
    }
}

So this will be run after all BeanPostProcessor have done their job. The setupInterceptors function will run when that party is over and install the interceptors beans. This use case may be extrapolated to cases like Security.

Conclusions:

  • If you are using a @Configuration extending from some class that runs some given functions automatically and you override them, you are probably inside of a BeanPostProcessor, so dont inject beans there and try to use AOP behaviour, because it wont work, and you will see Spring tells it to you with the beforementioned message in the console. In those cases dont use beans but objects (using the new clause).
  • If you need to use beans digg about which class is carrying the beans you want to setup at the end, @Autowired it and add those beans like i did before.

I hope this may save some time for you.

Solution 9 - Java

You need 3 lines of code for Async to work

  1. in applicationContext.xml
  2. At class level @EnableAsync
  3. @Async at method level

@Service @EnableAsync public myClass {

@Async public void myMethod(){

}

Solution 10 - Java

write a independent Spring configuration for asynchronous bean.
for example:

@Configuration
@ComponentScan(basePackages="xxxxxxxxxxxxxxxxxxxxx")
@EnableAsync
public class AsyncConfig {

    /**
     *  used by  asynchronous event listener.
     * @return
     */
    @Bean(name = "asynchronousListenerExecutor")
    public Executor createAsynchronousListenerExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setMaxPoolSize(100);
        executor.initialize();
        return executor;
    }
}

I overcome this problem with this situation.

Solution 11 - Java

Try below:

  1. In config create bean for ThreadPoolTaskExecutor

    @Bean(name = "threadPoolTaskExecutor") public Executor threadPoolTaskExecutor() { return new ThreadPoolTaskExecutor(); }

  2. In service method where @Async is used add

    @Async("threadPoolTaskExecutor") public void asyncMethod(){ //do something }

This should get @Async working.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEngineerBetter_DJView Question on Stackoverflow
Solution 1 - JavaShivan DragonView Answer on Stackoverflow
Solution 2 - JavaachView Answer on Stackoverflow
Solution 3 - JavatypoerrprView Answer on Stackoverflow
Solution 4 - JavaJiří VypědříkView Answer on Stackoverflow
Solution 5 - JavaKlesunView Answer on Stackoverflow
Solution 6 - JavacoderzView Answer on Stackoverflow
Solution 7 - JavaMikeView Answer on Stackoverflow
Solution 8 - JavaEliuXView Answer on Stackoverflow
Solution 9 - JavavsinghView Answer on Stackoverflow
Solution 10 - JavaChengView Answer on Stackoverflow
Solution 11 - JavaRavikView Answer on Stackoverflow