I'm trying to implement a very simple asynchronous method call in my SpringBoot application but, whatever I do, I just cannot get it to work. I've looked at dozens of tutorials but they all seem unnecessarily complicated for what I'm doing. I'd appreciate any help you could provide.
Essentially, when a certain REST endpoint of my SpringBoot app is hit, it kicks off a slow task. Instead of making the client wait for the slow task to finish before getting a response, I want to asynchronously call the task method, and return a response to the client right away. For simplicity, the response that I want to return right away is generic -- I don't care about returning the result of the slow task itself.
I'm using Java 8 and SpringBoot 2.1.7
Initially, I started with just 2 classes:
1) Class 1:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
2) Class 2:
@RestController
@RequestMapping("/rest")
public class Controller {
@RequestMapping(value = "/slowTask", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<?> slowTask() {
try{
asyncSlowTask();
// Want below response to be immediate -- instead, only occurs after above function is done.
return new ResponseEntity<String>("Accepted request to do slow task", HttpStatus.ACCEPTED);
}
catch (Exception e) {
return new ResponseEntity<String>("Exception", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@Async
public void asyncSlowTask(){
// Sleep for 10s
Thread.sleep(10000);
}
}
I tested this by hitting the app locally: curl http://localhost:8080/rest/slowTask
What I expected is that the curl command would return right away -- instead, it only returned after 10 seconds (after my slow task was done).
At this point, I read that the asynchronous method shouldn't be 'self-invoked' -- I had no idea if that's what I was doing but, to be safe, I moved the async method to a new class, so that my classes now looked like this:
1) Class 1:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
2) Class 2:
@RestController
@RequestMapping("/rest")
public class Controller {
@RequestMapping(value = "/slowTask", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<?> slowTask() {
try{
AsyncClass.asyncSlowTask();
// Want below response to be immediate -- instead, only occurs after above function is done.
return new ResponseEntity<String>("Accepted request to do slow task", HttpStatus.ACCEPTED);
}
catch (Exception e) {
return new ResponseEntity<String>("Exception", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
3) Class 3:
public class AsyncClass {
@Async
public static void asyncSlowTask(){
// Sleep for 10s
Thread.sleep(10000);
}
}
Unfortunately, this didn't make any difference -- the request is still returned only after my slow task is completed.
I've also tried a bunch of other minor variations (including putting @EnableSync and @Configuration on every class), but none of them worked.
So, here are my questions:
1) Did I need to move the async method to that 3rd class, or was my original, two-class setup good enough?
2) Which class should have the @EnableSync annotation?
I'm honestly completely lost as to where it should go -- all I know is that it should go in "one of your @Configuration classes", which means nothing to me.
3) Is there anything else I'm doing wrong??
Again, appreciate any help you can provide!