1 回答
TA贡献1770条经验 获得超3个赞
Callable 的问题在于调度程序 servlet 本身会启动异步处理,并且过滤器会在实际处理请求之前退出。
当 Callable 到达调度程序 servlet 时,它通过释放所有过滤器(过滤器基本上完成其工作)来从池中释放容器线程。当 Callable 产生结果时,会使用相同的请求再次调用调度程序 servlet,并且响应会立即由 Callable 返回的数据完成。这是由类型的 request 属性处理的,AsyncTaskManager该属性保存有关异步请求处理的一些信息。这可以用Filter和进行测试HandlerInterceptor。Filter只执行一次但HandlerInterceptor执行了两次(原始请求和Callable完成工作后的请求)
当您需要读取请求和响应时,解决方案之一是像这样重写dispatcherServlet:
@Bean
@Primary
public DispatcherServlet dispatcherServlet(WebApplicationContext context) {
return new DispatcherServlet(context) {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
super.service(requestWrapper, responseWrapper);
responseWrapper.copyBodyToResponse();
}
};
}
这样您就可以确保可以多次读取请求和响应。另一件事是像这样添加 HandlerInterceptor (您必须传递一些数据作为请求属性):
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
Exception {
Object asyncRequestData = request.getAttribute(LOGGER_FILTER_ATTRIBUTE);
if (asyncRequestData == null) {
request.setAttribute(LOGGER_FILTER_ATTRIBUTE, new AsyncRequestData(request));
}
return true;
}
@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex
) throws Exception {
Object asyncRequestData = request.getAttribute(LOGGER_FILTER_ATTRIBUTE);
if (asyncRequestData != null && response instanceof ContentCachingResponseWrapper) {
log(request, (ContentCachingResponseWrapper) response, (AsyncRequestData) asyncRequestData);
}
}
afterCompletion方法仅在异步请求完全处理后调用一次。preHandle被调用两次,因此您必须检查属性是否存在。在afterCompletion中,调用的响应已经存在,如果您确实想要替换它,您应该调用response.resetBuffer().
这是一种可能的解决方案,并且可能有更好的方法。
添加回答
举报