共计 6603 个字符,预计需要花费 17 分钟才能阅读完成。
介绍
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<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). | |
*/ | |
@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. | |
* <p>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
匹配对应的 controller
,HandlerMapping
就通过申请失去 HandlerExecutionChain
, 至于HandlerMapping
的源码剖析请参考其余文章。