介绍

HandlerInterceptor中文名拦截器,其实Interceptor也是拦截器的意思,这里加个Handler示意这只是一个接口,具体的实现须要自定义。

  • 机会:客户端拜访服务端的controller
  • 具体作用点:拜访前,拜访后
  • 作用形式:对request或response进行解决,加日志,包装返回值,增加申请头属性。

应用形式

以通用返回值为例。

1.自定义拦截器

实现HandlerInterceptor并注册为Bean

// 通用返回值拦截器@Componentpublic class CommonResultInterceptor implements HandlerInterceptor {    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)            throws Exception {        if (handler instanceof HandlerMethod) {            final HandlerMethod handlerMethod = (HandlerMethod) handler;            final Class<?> clazz = handlerMethod.getBeanType();            final Method method = handlerMethod.getMethod();            if (clazz.isAnnotationPresent(CommonResult.class)) {                request.setAttribute(RESPONSE_RESULT, clazz.getAnnotation(CommonResult.class));            } else if (method.isAnnotationPresent(CommonResult.class)) {                request.setAttribute(RESPONSE_RESULT, method.getAnnotation(CommonResult.class));            }        }        return true;    }}

2.增加拦截器

@Configurationpublic class WebAppConfig implements WebMvcConfigurer {    @Override    public void addInterceptors(InterceptorRegistry registry) {        CommonResultInterceptor interceptor = new CommonResultInterceptor();        registry.addInterceptor(interceptor);        WebMvcConfigurer.super.addInterceptors(registry);    }}

3.解决返回值

解决的形式有很多种,例如再加拦截器等等,这里我应用springboot的ResponseBodyAdvice接口

@ControllerAdvicepublic class CommonResultHandler implements ResponseBodyAdvice<Object> {    /**     * 判断是否要执行 beforeBodyWrite 办法     *     * @param methodParameter     * @param aClass     * @return true为执行,false不执行,有注解标记的时候解决返回值     */    @Override    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();        HttpServletRequest request = attributes.getRequest();        CommonResult commonResult = (CommonResult) request.getAttribute(CRM_RESPONSE_RESULT);        return Objects.nonNull(commonResult);    }    /**     * 对返回值做包装解决     *     * @return ResultBody     */    @Override    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType,                                  Class<? extends HttpMessageConverter<?>> aClass,                                  ServerHttpRequest request,                                  ServerHttpResponse response) {        if (body instanceof ResultBody) {            return body;        }        //当返回类型是String时,用的是StringHttpMessageConverter转换器,无奈转换为Json格局        //必须在办法体上标注RequestMapping(produces = "application/json; charset=UTF-8")        if (body instanceof String) {            String str = JSON.toJSONString(ResultBody.ok(body));            return str;        }        return ResultBody.ok(body);    }}

作用原理

1.申请首先会达到org.springframework.web.servlet.DispatcherServlet下的doDispatch办法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  HttpServletRequest processedRequest = request;  HandlerExecutionChain mappedHandler = null;  boolean multipartRequestParsed = false;  WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);  try {    ModelAndView mv = null;    Exception dispatchException = null;    try {      processedRequest = checkMultipart(request);      multipartRequestParsed = (processedRequest != request);      // Determine handler for the current request.      mappedHandler = getHandler(processedRequest);      if (mappedHandler == null) {        noHandlerFound(processedRequest, response);        return;      }      // Determine handler adapter for the current request.      HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());      // Process last-modified header, if supported by the handler.      String method = request.getMethod();      boolean isGet = "GET".equals(method);      if (isGet || "HEAD".equals(method)) {        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());        if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {          return;        }      }      // 调用已注册拦截器的prehandle办法      if (!mappedHandler.applyPreHandle(processedRequest, response)) {        return;      }      // 真正激活handler,也就是对应controller的办法      mv = ha.handle(processedRequest, response, mappedHandler.getHandler());      if (asyncManager.isConcurrentHandlingStarted()) {        return;      }      applyDefaultViewName(processedRequest, mv);                // 调用已注册拦截器的posthandle办法      mappedHandler.applyPostHandle(processedRequest, response, mv);    }    catch (Exception ex) {      dispatchException = ex;    }    catch (Throwable err) {      // As of 4.3, we're processing Errors thrown from handler methods as well,      // making them available for @ExceptionHandler methods and other scenarios.      dispatchException = new NestedServletException("Handler dispatch failed", err);    }    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  }  catch (Exception ex) {    triggerAfterCompletion(processedRequest, response, mappedHandler, ex);  }  catch (Throwable err) {    triggerAfterCompletion(processedRequest, response, mappedHandler,                           new NestedServletException("Handler processing failed", err));  }  finally {    if (asyncManager.isConcurrentHandlingStarted()) {      // 调用posthandle和aftercompletion办法      if (mappedHandler != null) {        mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);      }    }    else {      // Clean up any resources used by a multipart request.      if (multipartRequestParsed) {        cleanupMultipart(processedRequest);      }    }  }}

2.接着,咱们来看看preHandle办法的实现。

/**     * Apply preHandle methods of registered interceptors.     * @return {@code true} if the execution chain should proceed with the     * next interceptor or the handler itself. Else, DispatcherServlet assumes     * that this interceptor has already dealt with the response itself.     */boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {  HandlerInterceptor[] interceptors = getInterceptors();  if (!ObjectUtils.isEmpty(interceptors)) {    for (int i = 0; i < interceptors.length; i++) {      HandlerInterceptor interceptor = interceptors[i];      // 执行prehandle      if (!interceptor.preHandle(request, response, this.handler)) {        triggerAfterCompletion(request, response, null);        return false;      }      this.interceptorIndex = i;    }  }  return true;}

3.逻辑很简略,就是获取拦截器数组,而后调用preHandle办法,那么接下来看看getInterceptors办法

/**  * Return the array of interceptors to apply (in the given order).    */@Nullablepublic HandlerInterceptor[] getInterceptors() {  if (this.interceptors == null && this.interceptorList != null) {    this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[0]);  }  return this.interceptors;}

4.这里是间接调用this.interceptors,那么要害就在于HandlerExecutionChain的初始化怎么实现的

doDispatch办法中,有以下片段

// Determine handler for the current request.mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {  noHandlerFound(processedRequest, response);  return;}

这里getHandler正式获取了这个request对应的handler

/** * Return the HandlerExecutionChain for this request. * <p>Tries all handler mappings in order. * @param request current HTTP request * @return the HandlerExecutionChain, or {@code null} if no handler could be found */@Nullableprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {   if (this.handlerMappings != null) {      for (HandlerMapping mapping : this.handlerMappings) {         HandlerExecutionChain handler = mapping.getHandler(request);         if (handler != null) {            return handler;         }      }   }   return null;}

显然,在这里获取了request对应的所有的HandlerExecutionChain,察看到这是通过handlerMappings拿到HandlerMapping,再查问Handler。

handlerMappings的作用是通过urlpath匹配对应的controllerHandlerMapping就通过申请失去HandlerExecutionChain,至于HandlerMapping的源码剖析请参考其余文章。