当前位置: 首页 > 后端技术 > Java

HandlerInterceptor源码分析

时间:2023-04-01 18:10:24 Java

介绍HandlerInterceptor拦截器的中文名。其实Interceptor也有拦截器的意思。这里添加一个Handler意味着这只是一个接口,具体的实现需要定制。时机:客户端访问服务端的控制器具体作用:访问前,访问后作用方式:处理请求或响应,添加日志,封装返回值,添加请求头属性。使用方法以通用返回值为例。1.自定义拦截器实现HandlerInterceptor并注册为Bean//通用返回值拦截器@ComponentpublicclassCommonResultInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponse)Handlerifinstance,Objecthandler){finalHandlerMethodhandlerMethod=(HandlerMethod)处理程序;finalClassclazz=handlerMethod.getBeanType();final方法method=handlerMethod.getMethod();如果(clazz.isAnnotationPresent(CommonResult.class)){request.setAttribute(RESPONSE_RESULT,clazz.getAnnotation(CommonResult.class));}elseif(method.isAnnotationPresent(CommonResult.class)){request.setAttribute(RESPONSE_RESULT,method.getAnnotation(CommonResult.class));}}返回真;}}2.添加编译器@ConfigurationpublicclassWebAppConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){CommonResultInterceptor拦截器=newCommonResultInterceptor();registry.addInterceptor(拦截器);WebMvcConfigurer.super.addInterceptors(注册表);}}3.返回值的处理方式有很多种,比如添加拦截器等,这里我使用springboot的ResponseBodyAdvice接口@ControllerAdvicepublicclassCommonResultHandlerimplementsResponseBodyAdvice{/***判断是否执行beforeBodyWrite方法**@parammethodParameter*@paramaClass*@returntrue执行,false不执行,有注解标记时处理返回值*/@Overridepublicbooleansupports(MethodParametermethodParameter,Class>aClass){ServletRequestAttributes属性=(ServletRequestAttributes)RequestContextHolder.getRequestRequestRequestAttributes();ht=attributes.getRequest();CommonResultcommonResult=(CommonResult)request.getAttribute(CRM_RESPONSE_RESULT);返回Objects.nonNull(commonResult);}/***包装返回值**@returnResultBody*/@OverridepublicObjectbeforeBodyWrite(Objectbody,MethodParametermethodParameter,MediaTypemediaType,Class>aClass,ServerHttpRequestrequest,ServerHttpResponseresponse){if(bodyinstanceofResultBody){返回主体;}//当返回类型为String时,使用StringHttpMessageConverter转换器,不能转换成Json格式//必须在方法体上注解RequestMapping(produces="application/json;charset=UTF-8")if(bodyinstanceofString){Stringstr=JSON.toJSONString(ResultBody.ok(body));返回海峡;}返回ResultBody.ok(正文);}}工作原理1.请求会先到达org.springframework.web.servlet.DispatcherServlet下的doDispatch方法protectedvoiddoDispatch(HttpServletRequest请求,HttpServletResponse响应)抛出异常{HttpServletRequestprocessedRequest=request;HandlerExecutionChainmappedHandler=null;布尔multipartRequestParsed=false;WebAsyncManagerasyncManager=WebAsyncUtils.getAsyncManager(请求);尝试{ModelAndViewmv=null;异常dispatchException=null;尝试{processedRequest=checkMultipart(request);multipartRequestParsed=(processedRequest!=request);//确定当前请求的处理程序。mappedHandler=getHandler(processedRequest);if(mappedHandler==null){noHandlerFound(processedRequest,response);返回;}//确定当前请求的处理程序适配器。HandlerAdapterha=getHandlerAdapter(mappedHandler.getHandler());//如果处理程序支持,则处理最后修改的标头。字符串方法=request.getMethod();布尔值isGet="GET".equals(method);if(isGet||"HEAD".equals(method)){longlastModified=ha.getLastModified(request,mappedHandler.getHandler());if(newServletWebRequest(request,response).checkNotModified(lastModified)&&isGet){return;}}//调用注册拦截器的prehandle方法if(!mappedHandler.applyPreHandle(processedRequest,response)){return;}//真正激活handler,也就是对应的方法到控制器mv=ha.handle(processedRequest,response,mappedHandler.getHandler());if(asyncManager.isConcurrentHandlingStarted()){return;}applyDefaultViewName(processedRequest,mv);//调用注册拦截器mappedHandler的posthandle方法.applyPostHandle(processedRequest,response,mv);}catch(Exceptionex){dispatchException=ex;}catch(Throwableerr){//从4.3开始,我们也在处理从处理程序方法中抛出的错误,//em可用于@ExceptionHandler方法和其他场景。dispatchException=newNestedServletException("Handlerdispatchfailed",err);}processDispatchResult(processedRequest,response,mappedHandler,mv,dispatchException);}catch(Exceptionex){triggerAfterCompletion(processedRequest,response,mappedHandler,ex);}catch(Throwableerr){triggerAfterCompletion(processedRequest,response,mappedHandler,newNestedServletException("Handlerprocessingfailed",err));}finally{if(asyncManager.isConcurrentHandlingStarted()){//调用posthandle和aftercompletion方法if(mappedHandler!=null){mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest,response);}}else{//清除多部分请求使用的所有资源。如果(multipartRequestParsed){cleanupMultipart(processedRequest);}}}}2.接着,我下面看一下preHandle方法的实现/***应用已注册拦截器的preHandle方法。*@return{@codetrue}如果执行链应该继续下一个拦截器或处理程序本身。否则,DispatcherServlet假定*这个拦截器已经处理了响应本身。*/booleanapplyPreHandle(HttpServletRequest请求,HttpServletResponse响应)throwsException{HandlerInterceptor[]拦截器=getInterceptors();if(!ObjectUtils.isEmpty(interceptors)){for(inti=0;i按顺序尝试所有处理程序映射。*@param请求当前HTTP请求*@returnHandlerExecutionChain,如果找不到处理程序,则返回{@codenull}*/@NullableprotectedHandlerExecutionChaingetHandler(HttpServletRequestrequest)throwsException{if(this.handlerMappings!=null){for(HandlerMapping映射器:this.hand){HandlerExecutionChainhandler=mapping.getHandler(request);如果(处理程序!=null){返回处理程序;}}}returnnull;}很明显,这里获取了request对应的所有HandlerExecutionChain,观察到这个是通过handlerMappingsHandlerMapping获取的,然后通过urlpath查询HandlerhandlerMappings匹配对应的controller,HandlerMapping会通过request获取HandlerExecutionChain,关于HandlerMapping的源码分析,请参考其他文章。