【问题标题】:Spring Boot runs filter twice for requests returning a CompletionStageSpring Boot 为返回 CompletionStage 的请求运行两次过滤器
【发布时间】: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";
    };
}

所以,我不确定很多事情。

  1. 似乎CallableCompletionStage 在执行它的线程的上下文中被区别对待。
  2. 如果是这样,那为什么我的过滤器每次都运行两次?如果过滤器的工作是设置某个特定于请求的上下文,那么如果 CompletionStage 无论如何都无法访问,那么再次运行它是没有意义的。
  3. 究竟为什么过滤器会以哪种方式运行两次?

【问题讨论】:

    标签: java spring spring-mvc


    【解决方案1】:

    请将GenericFilterBean 替换为OncePerRequestFilter

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-05
      • 2019-06-02
      • 2021-07-14
      • 1970-01-01
      • 2020-03-24
      • 2023-04-09
      • 2019-11-13
      • 2017-01-13
      相关资源
      最近更新 更多