如何在spring mvc中的操作之前发送响应
Posted
技术标签:
【中文标题】如何在spring mvc中的操作之前发送响应【英文标题】:How to send response before actions in spring mvc 【发布时间】:2015-11-11 09:08:28 【问题描述】:说我的spring控制器函数接收到大量数据。 鉴于数据结构正确,我想返回 200 OK,并且在那之后我想执行处理,这可能需要一段时间。
据我了解,发送响应的唯一方法是通过 return 命令。但我不想在响应发送时结束该功能。
还有其他方法可以在函数中间向客户端发送响应吗?
创建一个新的线程运行是显而易见的,但其他语言 (JS) 可以让您更优雅地处理它。
@RequestMapping(value = Connectors.CONNECTOR_HEARTBEAT, method = RequestMethod.POST)
public ResponseEntity<String> doSomething(@RequestBody List<Message> messages)
HttpStatus code = (messages!=null && !messages.isEmpty()) ? HttpStatus.OK
: HttpStatus.NOT_FOUND;
return new ResponseEntity<String>(res, code);
// how do I add code here??
【问题讨论】:
使用@Async 方法可以包装你想运行的所有东西,它将被不同的线程处理 This might help 【参考方案1】:你可以使用@Async
@RequestMapping(value = Connectors.CONNECTOR_HEARTBEAT, method =
RequestMethod.POST)
public ResponseEntity<String> doSomething(@RequestBody List<Message>
messages)
do();
HttpStatus code = (messages!=null && !messages.isEmpty()) ? HttpStatus.OK
: HttpStatus.NOT_FOUND;
return new ResponseEntity<String>(res, code);
@Async
void do()
//your code
这项工作在 java 8 中
【讨论】:
【参考方案2】:您应该使用HandlerInterceptor
。但是代码比预期的要复杂一些。因此,这里有一个代码建议,通过将整个解决方案放在一个类中来使其更简单:
@RequestMapping(value = Connectors.CONNECTOR_HEARTBEAT, method = RequestMethod.POST)
public ResponseEntity<String> doSomething(@RequestBody List<Message> messages)
HttpStatus code = (messages!=null && !messages.isEmpty()) ? HttpStatus.OK
: HttpStatus.NOT_FOUND;
result.set(res); // Save the object to be used after response
return new ResponseEntity<String>(res, code);
private static final ThreadLocal<String> result = new ThreadLocal<String>();
@Bean
public HandlerInterceptor interceptor()
return new HandlerInterceptor()
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
// Get the saved object and clean for the next request
String res = result.get();
result.set(null);
// TODO Your code to be executed after response.
;
【讨论】:
【参考方案3】:您当然可以在发送响应后进行处理。更通用的方法是使用HandlerInterceptor
的afterCompletion
方法。通过构造,它将在响应发送到客户端后执行,但它会强制您将逻辑拆分为 2 个组件,即控制器中的 before 部分和 after参与拦截器。
另一种方法是忘记 Spring MVC 机制并在控制器中手动提交响应:
@RequestMapping(value = Connectors.CONNECTOR_HEARTBEAT, method = RequestMethod.POST)
public void doSomething(@RequestBody List<Message> messages, HttpServletResponse response)
int code = (messages!=null && !messages.isEmpty()) ? HttpServletResponse.SC_OK
: HttpServletResponse.SC_NOT_FOUND;
if (code != HttpServletResponse.SC_OK)
response.sendError(code, res);
return;
java.io.PrintWriter wr = response.getWriter();
response.setStatus(code);
wr.print(res);
wr.flush();
wr.close();
// Now it it time to do the long processing
...
注意 void 返回代码以通知 Spring 响应已在控制器中提交。
作为一个附带优势,处理仍然发生在同一个线程中,因此您可以完全访问会话范围的属性或 Spring MVC 或 Spring Security 使用的任何其他线程局部变量...
【讨论】:
如果请求来自两个不同的用户,那么它会共享相同的变量还是为不同的用户共享不同的变量?? 你用过变量 res,那个变量指的是什么? 我不知道。它在原始问题中,所以我将其保留在我的答案中。我只是假设这是一条用于提供响应的消息。 嗨@SergeBallesta,我尝试了您的解决方案,但没有奏效。在 wr.close() 之后,我将 sleep 方法放置了 10 秒。当我点击请求时,它会等待 10 秒发送回响应。 PFB 代码。输出流.close(); System.out.println("流关闭。"); System.out.println("睡觉");尝试 Thread.sleep(10000); catch (InterruptedException e) e.printStackTrace(); System.out.println("唤醒"); @SergeBallesta,如果我遗漏了什么,请帮忙。【参考方案4】:我猜你mau使用spring的异步机制 servlet 3.0 中引入了异步方法,Spring 为它们提供了一些支持 基本上……你提出请求;请求由服务器处理,然后在后台,一个新线程管理请求数据 这是一个有用的链接(至少我希望:))http://spring.io/blog/2012/05/10/spring-mvc-3-2-preview-making-a-controller-method-asynchronous/
【讨论】:
以上是关于如何在spring mvc中的操作之前发送响应的主要内容,如果未能解决你的问题,请参考以下文章