My [basic] Spring Boot application accepts a request from the browser, sent via jQuery.get()
and is supposed to immediately receive a response - such as \"y
I used spring-boot main class to define Asynchronous configuration. The @EnableAsync
annotation enables 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 searched by Spring.
Spring-Boot-Application.class
@SpringBootApplication
@EnableAsync
public class AsyncApplication {
@Value("${config.threadpool.corepool.size}")
private Integer corePoolSize;
@Value("${config.threadpool.maxpool.size}")
private Integer maxPoolSize;
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
//name of below method should not be changed.
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
//other proeprties to be set here
executor.setThreadNamePrefix("ASYNC-");
executor.initialize();
return executor;
}
}
In Implementation, use @Async
at method level to make the method Asynchronous. Methods need to be public to use @Async
. Also, @Async
annotated method calling @Async
method will not work.
Sample implementation for reference below -
@Async
public void updateData(String userId) throws ApplicationException {
logger.info("Updating details for User with {}", userId);
//your code goes here...
}
The configuration properties are defined in application.properties
file
#Core Pool Size for Async
config.threadpool.corepool.size=100
#Max Pool Size for Async
config.threadpool.maxpool.size=400
For Rules on how to define the pool, please refer to rules-of-a-threadpool-executor
I had a similar issue and I had the annotations @Async and @EnableAsync in the correct beans and still the method was executing synchronously. After I checked the logs there was a warning saying that I had more than one bean of type ThreadPoolTaskExecutor and none of them called taskExecutor So...
@Bean(name="taskExecutor")
public ThreadPoolTaskExecutor defaultTaskExecutor() {
ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
//Thread pool configuration
//...
return pool;
}
See http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.html for the configuration available for the thread pool.
For all those who are still looking for all the steps in @Asnyc explained in a simple way, here is the answer:
Here is a simple example with @Async. Follow these steps to get @Async to work in your Spring Boot application:
Step 1: Add @EnableAsync annotation and Add TaskExecutor Bean to Application Class.
Example:
@SpringBootApplication
@EnableAsync
public class AsynchronousSpringBootApplication {
private static final Logger logger = LoggerFactory.getLogger(AsynchronousSpringBootApplication.class);
@Bean(name="processExecutor")
public TaskExecutor workExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(AsynchronousSpringBootApplication.class,args);
}
}
Step 2: Add Method which executes an Asynchronous Process
@Service
public class ProcessServiceImpl implements ProcessService {
private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);
@Async("processExecutor")
@Override
public void process() {
logger.info("Received request to process in ProcessServiceImpl.process()");
try {
Thread.sleep(15 * 1000);
logger.info("Processing complete");
}
catch (InterruptedException ie) {
logger.error("Error in ProcessServiceImpl.process(): {}", ie.getMessage());
}
}
}
Step 3: Add an API in the Controller to execute the asynchronous processing
@Autowired
private ProcessService processService;
@RequestMapping(value = "ping/async", method = RequestMethod.GET)
public ResponseEntity<Map<String, String>> async() {
processService.process();
Map<String, String> response = new HashMap<>();
response.put("message", "Request is under process");
return new ResponseEntity<>(response, HttpStatus.OK);
}
I have also written a blog and a working application on GitHub with these steps. Please check: http://softwaredevelopercentral.blogspot.com/2017/07/asynchronous-processing-async-in-spring.html
You are calling the @Async
method from another method in the same class. Unless you enable AspectJ proxy mode for the @EnableAsync
(and provide a weaver of course) that won't work (google "proxy self-invocation"). The easiest fix is to put the @Async
method in another @Bean
.
As code sample for @dave-syer answer:
This works asynchronously:
private void longRunning() {
try {
log.info("wait 3 seconds");
Thread.sleep(3000);
} catch (InterruptedException e1) {
}
log.info("done");
}
@Async
@Override
public void doWork() {
longRunning();
}
But this doesn't:
@Async
private void longRunning() {
try {
log.info("wait 3 seconds");
Thread.sleep(3000);
} catch (InterruptedException e1) {
}
log.info("done");
}
@Override
public void doWork() {
longRunning();
}
Follow the three steps :
1 Step : Use @EnableAsync with @configuration or @SpringBootApplication
@EnableAsync public class Application {
2 Step :
/**
* THIS FOR ASYNCRONOUS PROCESS/METHOD
* @return
*/
@Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("Asynchronous Process-");
executor.initialize();
return executor;
}
3 Step : Put @Async over the intended method
T