Spring Controller在响应发送后开始处理

时间:2014-12-16 15:32:54

标签: java spring-mvc concurrency

我正在使用Spring MVC控制器,并希望在新线程中开始执行任务。但是线程不应该立即启动,而是仅在响应发送到客户端之后。

手段 - 按严格的时间顺序:

  1. request
  2. 返回新的ResponseEntity ... / client接收Http状态200 ok。
  3. 任务的处理开始。
  4. 我如何实现这一目标?

    我想使用spring的异步抽象,调用一个用@Async注释的方法,但这样我就不能保证新线程等待响应被发送。

4 个答案:

答案 0 :(得分:11)

您可以使用拦截器。在Spring MVC中处理请求的顺序是:

  • DispatcherServlet获取请求,响应对并确定处理
  • [可选]拦截器preHandle被调用(带有停止处理的选项)
  • 控制器被称为
  • [可选]拦截器postHandle称为
  • ViewResolver和view执行实际的响应处理并发送响应
  • [可选] interinterors afterCompletion被称为

以上内容已经过度简化,目的只是为了显示拦截器afterCompletion方法在响应发送给客户端之后被调用,具有以下签名:

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

在该方法中,您可以在开始处理之前测试异常的发生和响应的正确性(ex == null && response.getStatus == HttpServletResponse.SC_OK)。

答案 1 :(得分:5)

如果您的“发送回复后”要求通过“在呈现视图后”完成,则您可以使用HandlerInterceptor的实现。举个例子比较Spring 3 MVC Interceptor tutorial with example,在afterCompletion触发您的工作。

如果你的工作需要在“电线接通”之后触发,我想知道原因。

答案 2 :(得分:1)

HandlerInterceptor是解决方案,但代码比预期的要复杂一点。

这里提供了一个代码建议,通过将整个解决方案放在一个类中来简化它:

@RequestMapping("/mypath")
public Object execute() throws Exception {
    Object obj = new Object();
    result.set(obj); // Save the object to be used after response
    return obj;
}

private static final ThreadLocal<Object> result = new ThreadLocal<Object>();

@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 :(得分:0)

您可以在创建响应实体之前将任务添加到阻止队列。让任务执行器定期(每隔x秒)运行一次队列并轮询任务。如果找到任务,则会执行该任务。如果没有,则线程完成其run方法并等待下一次运行(在x秒内)。

如何定期运行任务: http://www.mkyong.com/java/how-to-run-a-task-periodically-in-java/

在控制器和任务执行程序服务中将队列作为依赖项注入。这应该是一个简单的解决方案。

在这个szenario中,您无法确定客户端是否收到请求。但是如果你想要安全(r),请在任务对象中添加一个足够的偏移量(例如当前时间+30秒)。让任务执行者检查轮询任务的截止日期是现在还是过去。否则忽略此次运行的任务。