Spring Controller initial processing after sending a response

I am using Spring MVC and want to start the task in a new thread. However, the task should not begin immediately, but only after the response has been sent to the client.

Sequence - in a strict temporary order:

  1. request
  2. return a new ResponseEntity... / client receives an HTTP status of 200 OK.
  3. job processing begins.

How can I achieve this?

I wanted to use Spring's asynchronous abstraction by calling a method annotated by @Async, but it does not guarantee that the new thread will wait for the response to be sent first.

+10
source share
4 answers

You can use an interceptor for this. The order of events for request processing in Spring MVC:

  • DispatcherServlet receives a pair of Request, Response and defines processing
  • [optional] called preHandle interceptors (with the possibility of stopping processing)
  • the controller is called
  • [optional] called postHandle interceptors
  • ViewResolver and view do the actual processing of the response and send the response
  • [optional] called afterCompletion interceptors

afterCompletion too afterCompletion and is intended only to show that the afterCompletion methods after sending a response to the client with the following signature:

 void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception 

In this method, you can check for an exception and the correct response ( ex == null && response.getStatus() == HttpServletResponse.SC_OK ) before starting the processing.

+12
source

If your request “after the response is sent” is satisfied with “after the view has been processed”, you can use the HandlerInterceptor implementation. For example, cf. Spring 3 MVC Interceptor tutorial with an example of starting your work in afterCompletion .

If your work needs to be started “after it works,” I would like to know why.

+5
source

HandlerInterceptor is a solution, but the code is getting a bit more complicated than expected.

Here is a code sentence to simplify it by putting the entire solution in one class:

 private static final ThreadLocal<Object> result = new ThreadLocal<Object>(); @RequestMapping("/mypath") public Object execute() throws Exception { Object obj = new Object(); result.set(obj); // Save the object to be used after response return obj; } @Bean public MappedInterceptor interceptor() { return new MappedInterceptor(Arrays.array("/mypath"), new HandlerInterceptor() { @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // Get the saved object Object results = result.get(); // Clean for the next request result.set(null); // TODO Your code to be executed after response. } }); } 
+3
source

You can add a task to the lock queue before the response object is created. Let the task performer run periodically (every x seconds) in turn and poll for tasks. If the task is found, it will be completed. If not, the thread finishes its start method and waits for the next run (after x seconds).

How to run the task periodically: http://www.mkyong.com/java/how-to-run-a-task-periodically-in-java/

Enter the queue as a dependency both in the controller and in the service of the task executor. It should be an easy solution to get started.

With this szenario, you cannot be sure that the client is receiving a request. But if you want to be safe (r), add the execution date to the task object with a sufficient offset (for example, the current time + 30 seconds). Let the task performer check whether the survey completion date matches the given or the past. Otherwise, ignore the task for this run.

0
source

Source: https://habr.com/ru/post/979705/


All Articles