介绍
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
的源码分析请参考其他文章。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。