【发布时间】:2017-03-01 09:52:45
【问题描述】:
当方法返回 CompletionStage 时,我的过滤器运行了两次。从RequestMapping (here) 上的文档来看,它是受支持的返回值。
CompletionStage(例如由 CompletableFuture 实现),应用程序使用它在自己选择的单独线程中生成返回值,作为返回 Callable 的替代方法。
由于项目非常复杂,包含大量并发代码,因此我创建了一个新的简单 spring-boot 项目。这是其中的(唯一)控制器:
@Controller
public class BaseController {
@RequestMapping("/hello")
@ResponseBody
public CompletionStage<String> world() {
return CompletableFuture.supplyAsync(() -> "Hello World");
}
}
还有一个过滤器:
@WebFilter
@Component
public class GenericLoggingFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
System.out.println(httpServletRequest.getMethod() + " " +
httpServletRequest.getRequestURI());
chain.doFilter(request, response);
}
}
当我拨打curl http://localhost:8080/hello 时,它会在控制台上打印两次GET /hello。当我更改控制器方法以返回 String:
@RequestMapping("/hello")
@ResponseBody
public String world() {
return "Hello World";
}
它只打印一次。即使我将其更改为 Callable,也会显示此行为,这并没有真正的并发意义(当然,spring 本身现在可能会将其视为 Async 请求)。
所以,如果 spring 再次运行整个 web 堆栈以提供可用的请求上下文,即使这样也没有任何意义,因为以下原因:
@RequestMapping("/hello")
@ResponseBody
public CompletionStage<String> world() {
return CompletableFuture.supplyAsync(() -> {
System.out.println(RequestContextHolder.currentRequestAttributes());
return "Hello World";
});
}
抛出异常:IllegalStateException: No thread-bound request found...
令人惊讶的是,以下工作:
@RequestMapping("/hello")
@ResponseBody
public Callable<String> world() {
return () -> {
System.out.println(RequestContextHolder.currentRequestAttributes());
return "Hello World";
};
}
所以,我不确定很多事情。
- 似乎
Callable和CompletionStage在执行它的线程的上下文中被区别对待。 - 如果是这样,那为什么我的过滤器每次都运行两次?如果过滤器的工作是设置某个特定于请求的上下文,那么如果
CompletionStage无论如何都无法访问,那么再次运行它是没有意义的。 - 究竟为什么过滤器会以哪种方式运行两次?
【问题讨论】:
标签: java spring spring-mvc