DeferredResult接口执行两次拦截器的问题

/ 后端 / 没有评论 / 29浏览

发现问题,某些使用了DeferredResult的接口,每次打印两次日志,日志打印操作在拦截器中,进而发现其实是拦截器执行了两次,过滤器正常执行一次;

1.解决方案:

(1)由于异步请求使用了两个不同线程每个线程会执行一次拦截器preHandle,普通拦截器判断分发器类型,如果是异步请求进行直接放行,此时第二次重复进入拦截器则无需重复执行拦截器逻辑,注意做到幂等:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
    DispatcherType dispatcherType = request.getDispatcherType();
    if (dispatcherType.equals(DispatcherType.ASYNC)){
        return true;
    }
    //其他代码....
}

(2)根据业务逻辑适当使用AsyncHandlerInterceptor拦截器的afterConcurrentHandlingStarted方法,该方法仅执行一次,是在tomcat处理请求逻辑线程中执行,并且在线程池子线程执行前执行;

//同一个线程A
AsyncTestInterceptor执行了preHandle
//这中间执行接口的同步代码
AsyncTestInterceptor执行了afterConcurrentHandlingStartedThread
过滤器执行完成

//同一个线程B
//接下来执行异步代码
AsyncTestInterceptor执行了preHandle
AsyncTestInterceptor执行了postHandle
AsyncTestInterceptor执行了afterCompletion

2.测试

添加一个过滤器:

public class CrosFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        chain.doFilter(request, responseServlet);
        System.out.println("过滤器执行了完毕");
    }
}

测试接口

    @RequestMapping("/test")
    public DeferredResult<String> test() {
        DeferredResult<String> result = new DeferredResult<>(1000L * 3);
        result.onTimeout(() -> {
            result.setResult("接口超时");
        });
        //使用线程池执行
        WfThreadPoolCallerRunsPolicyUtil.getPool().execute(() -> {
            result.setResult("执行成功");
        });
        return result;
    }

(1)普通拦截器执行顺序:

public class TestInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("TestInterceptor执行了preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("TestInterceptor执行了postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("TestInterceptor执行了afterCompletion");
    }
}

执行结果(preHandle执行两次):

TestInterceptor执行了preHandle

过滤器执行了完毕

TestInterceptor执行了preHandle
TestInterceptor执行了postHandle
TestInterceptor执行了afterCompletion

(2)异步拦截器:

public class AsyncTestInterceptor implements AsyncHandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("AsyncTestInterceptor执行了preHandle");
        return true;
    }

    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("AsyncTestInterceptor执行了afterConcurrentHandlingStarted");
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("AsyncTestInterceptor执行了postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("AsyncTestInterceptor执行了afterCompletion");
    }
}

执行结果(preHandle执行两次):

AsyncTestInterceptor执行了preHandle
AsyncTestInterceptor执行了afterConcurrentHandlingStarted

过滤器执行了完毕

AsyncTestInterceptor执行了preHandle
AsyncTestInterceptor执行了postHandle
AsyncTestInterceptor执行了afterCompletion