[英]Spring Controller start processing after response is sent
I am using a Spring MVC controller and want to start the execution of a task in a new thread.我正在使用 Spring MVC 控制器并希望在新线程中开始执行任务。 However the task should not start immediately but only after the response has been sent to the client.然而,任务不应立即开始,而应在响应发送到客户端后开始。
The sequence - in strict temporal order:序列 - 严格按照时间顺序:
ResponseEntity ...
/ client receives HTTP status 200 OK. return new ResponseEntity ...
/ 客户端收到 HTTP 状态 200 OK。How do I achieve this?我如何实现这一目标?
I wanted to use Spring's async abstraction by calling a method annotated with @Async, but it does not guarantee that the new thread waits for the response to be sent first.我想通过调用一个用@Async 注释的方法来使用Spring 的异步抽象,但它不保证新线程首先等待响应被发送。
You can use an interceptor for that.您可以为此使用拦截器。 The order of events for handling a request in Spring MVC is:在 Spring MVC 中处理请求的事件顺序是:
The above is over simplified and is just aimed at showing that interceptor afterCompletion
methods are called after the response has been sent to client, with following signature :以上过于简化,只是为了表明在响应发送到客户端后afterCompletion
拦截器afterCompletion
方法,具有以下签名:
void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex)
throws Exception
In that method, you can test the occurence of an exception and the correctness of the response ( ex == null && response.getStatus() == HttpServletResponse.SC_OK
) before starting your processing.在该方法中,您可以在开始处理之前测试异常的发生和响应的正确性( ex == null && response.getStatus() == HttpServletResponse.SC_OK
)。
If your "after respond is sent" requirement is fulfilled with "after the view has been rendered" you may use an implementation of HandlerInterceptor .如果您的“发送响应后”要求满足“在呈现视图之后”,您可以使用HandlerInterceptor的实现。 For an example cf.例如,参见。 Spring 3 MVC Interceptor tutorial with example , triggering your job in afterCompletion
. Spring 3 MVC 拦截器教程与示例,在afterCompletion
触发您的工作。
If your job needs to be triggered "after it hit the wire", I'd like to know why.如果您的工作需要“在它碰到电线后”触发,我想知道为什么。
The HandlerInterceptor
is the solution, but the code get a little bit more complex than expected. HandlerInterceptor
是解决方案,但代码比预期的要复杂一些。
Here's a code suggestion to make it simpler by putting the whole solution in a single 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.
}
});
}
You could add the task to a blocking queue, before the response entity is created.在创建响应实体之前,您可以将任务添加到阻塞队列中。 Let a task executor run periodically (every x seconds) over the queue and poll for tasks.让任务执行器定期(每 x 秒)在队列上运行并轮询任务。 If a task is found, it will be executed.如果找到一个任务,它将被执行。 If not, the thread finishes its run method and waits for the next run (in x seconds).如果没有,线程完成其 run 方法并等待下一次运行(以 x 秒为单位)。
How to run a task periodically: http://www.mkyong.com/java/how-to-run-a-task-periodically-in-java/如何定期运行任务: http : //www.mkyong.com/java/how-to-run-a-task-periodically-in-java/
Inject the queue as dependency in both the controller and the task executor service.将队列作为依赖项注入控制器和任务执行器服务。 This should be an easy solution to start with.这应该是一个简单的解决方案。
In this szenario you can't be sure, that the client receives the request.在这个场景中,您无法确定客户端是否收到了请求。 But if you want to be safe(r), add a due date to your task object with a sufficient offset (eg current time + 30 seconds).但是,如果您想安全(r),请为您的任务对象添加一个具有足够偏移量的截止日期(例如当前时间 + 30 秒)。 Let the task executor check if the due date of the polled task is now or in the past.让任务执行器检查轮询任务的截止日期是现在还是过去。 Otherwise ignore the task for this run.否则忽略此运行的任务。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.