我遇到了一个问题,即在方法返回CompletionStage
时,我的筛选器运行了两次。从RequestMapping
(here)的文档,它是一个支持的返回值。Spring Boot为请求返回CompletionStage运行两次筛选器
一个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
,但没有真正的并发意义(当然,弹簧本身可能将其视为Async
请求)。
所以,如果弹簧再次运行整个网络栈有一个请求上下文中可用,甚至没有什么意义,因为以下几点:
@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
没有意义。 - 为什么过滤器会以两种方式运行两次?