介绍
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
匹配对应的controller
,HandlerMapping
就通过申请失去HandlerExecutionChain
,至于HandlerMapping
的源码剖析请参考其余文章。