HandlerInterceptor 源码解析

介绍

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

  • 时机:客户端访问服务端的controller
  • 具体作用点:访问前,访问后
  • 作用方式:对request或response进行处理,加日志,包装返回值,添加请求头属性。

使用方式

以通用返回值为例。

1.自定义拦截器

实现HandlerInterceptor并注册为Bean

// 通用返回值拦截器
@Component
public 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.添加拦截器

@Configuration
public class WebAppConfig implements WebMvcConfigurer {

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

3.处理返回值

处理的方式有很多种,例如再加拦截器等等,这里我使用springboot的ResponseBodyAdvice接口

@ControllerAdvice
public class CommonResultHandler implements ResponseBodyAdvice {
    /**
     * 判断是否要执行 beforeBodyWrite 方法
     *
     * @param methodParameter
     * @param aClass
     * @return true为执行,false不执行,有注解标记的时候处理返回值
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class> 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> 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).
    */
@Nullable
public 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.
 * 

Tries all handler mappings in order. * @param request current HTTP request * @return the HandlerExecutionChain, or {@code null} if no handler could be found */ @Nullable protected 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的源码分析请参考其他文章。

你可能感兴趣的:(HandlerInterceptor 源码解析)