springmvc支持服务端在处理业务逻辑过程中出现异常的时候可以配置相应的ModelAndView对象返回给客户端,本文介绍springmvc默认的几种HandlerExceptionResolver类
springmvc的xml配置化-Exception配置
<beanid="exceptionHandler"class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!--设置默认返回viewName,通常与freemarker引擎搭配使用--> <propertyname="defaultErrorView"value="error/defaultError"/> <!--设置默认返回responsestatus--> <propertyname="defaultStatusCode"value="500"/> <!--配置相应的异常类与viewName的映射--> <propertyname="exceptionMappings"> <props> <propkey="SessionTimeoutException">redirect:../login.html</prop> <propkey="AuthenticationException">error/403</prop> </props> </property> </bean>以上的配置会对出SessionTimeoutException异常则跳转至login页面,对AuthenticationException异常则跳转至403页面,对其他的异常则默认跳转至defaultError页面呈现并返回500的错误码
接口内只有一个方法resolveException(),通过解析异常查询配置以得到符合条件的ModelAndView对象
/** *Trytoresolvethegivenexceptionthatgotthrownduringhandlerexecution, *returninga{@linkModelAndView}thatrepresentsaspecificerrorpageifappropriate. *<p>Thereturned{@codeModelAndView}maybe{@linkplainModelAndView#isEmpty()empty} *toindicatethattheexceptionhasbeenresolvedsuccessfullybutthatnoview *shouldberendered,forinstancebysettingastatuscode. *@paramrequestcurrentHTTPrequest *@paramresponsecurrentHTTPresponse *@paramhandlertheexecutedhandler,or{@codenull}ifnonechosenatthe *timeoftheexception(forexample,ifmultipartresolutionfailed) *@paramextheexceptionthatgotthrownduringhandlerexecution *@returnacorresponding{@codeModelAndView}toforwardto,or{@codenull} *fordefaultprocessing */ ModelAndViewresolveException( HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex);所有的spring内置异常解析类都继承于此,直奔主题看resolveException()方法
@Override publicModelAndViewresolveException(HttpServletRequestrequest,HttpServletResponseresponse, Objecthandler,Exceptionex){ //判断是否需要解析 if(shouldApplyTo(request,handler)){ //此处一般是判断内部属性preventResponseCaching是否为true,是则设置响应包头cache-control:no-store prepareResponse(ex,response); //使用模板方法doResolveException()方法供子类实现 ModelAndViewresult=doResolveException(request,response,handler,ex); if(result!=null){ //日志打印一发 logException(ex,request); } returnresult; } else{ returnnull; } }附带着分析下shouldApplyTo()方法
/** **可以配置mappedHandlers和mappedHandlerClasses属性来特定匹配 **默认情况下两者都为空则直接返回true,表明对所有的handler都进行异常解析 */ protectedbooleanshouldApplyTo(HttpServletRequestrequest,Objecthandler){ //此处的handler一般为bean对象 if(handler!=null){ if(this.mappedHandlers!=null&&this.mappedHandlers.contains(handler)){ returntrue; } if(this.mappedHandlerClasses!=null){ for(Class<?>handlerClass:this.mappedHandlerClasses){ if(handlerClass.isInstance(handler)){ returntrue; } } } } //Elseonlyapplyiftherearenoexplicithandlermappings. return(this.mappedHandlers==null&&this.mappedHandlerClasses==null); }比较简单的实现类,可以配绑定viewName和exception以完成简单的异常映射视图页面
protectedModelAndViewdoResolveException(HttpServletRequestrequest,HttpServletResponseresponse, Objecthandler,Exceptionex){ //ExposeModelAndViewforchosenerrorview. StringviewName=determineViewName(ex,request); if(viewName!=null){ //如果配置了statusCodes属性,则对此异常的状态码进行设置 IntegerstatusCode=determineStatusCode(request,viewName); if(statusCode!=null){ applyStatusCodeIfPossible(request,response,statusCode); } //创建ModelAndView对象 returngetModelAndView(viewName,ex,request); } else{ returnnull; } }针对以上的源码我们分两步去简单分析下
SimpleMappingExceptionResolver#determineViewName()-找寻viewName protectedStringdetermineViewName(Exceptionex,HttpServletRequestrequest){ StringviewName=null; //判断异常是否属于excludeExceptions集合内,是则直接返回null if(this.excludedExceptions!=null){ for(Class<?>excludedEx:this.excludedExceptions){ if(excludedEx.equals(ex.getClass())){ returnnull; } } } //Checkforspecificexceptionmappings. //从exceptionMappings集合内根据exception获取到相应的viewName if(this.exceptionMappings!=null){ viewName=findMatchingViewName(this.exceptionMappings,ex); } //当exceptionMappings集合内不存在指定的exception但是默认视图指定则直接返回默认视图 if(viewName==null&&this.defaultErrorView!=null){ viewName=this.defaultErrorView; } returnviewName; } excludedExceptions集合可以过滤指定的exception,对其不进行解析直接返回null。可配置 exceptionMappings绑定了exception与viewName的关系,如果在其集合内没找到相应的viewName,但是defaultErrorView属性指定,则会直接返回defaultErrorView对应的视图 SimpleMappingExceptionResolver#getModelAndView()-创建ModelAndView对象 protectedModelAndViewgetModelAndView(StringviewName,Exceptionex){ ModelAndViewmv=newModelAndView(viewName); //exceptionAttribute默认为exception if(this.exceptionAttribute!=null){ //将exception信息添加到model中 mv.addObject(this.exceptionAttribute,ex); } returnmv; }主要就是将exceptionAttribute对应的参数值默认为exception属性添加到视图对象的model中
主要是解析带有@ResponseStatus的异常类,将其中的异常信息描述直接返回给客户端
@Override protectedModelAndViewdoResolveException(HttpServletRequestrequest,HttpServletResponseresponse, Objecthandler,Exceptionex){ //获取相应类上的注解@ResponseStatus ResponseStatusresponseStatus=AnnotatedElementUtils.findMergedAnnotation(ex.getClass(),ResponseStatus.class); if(responseStatus!=null){ try{ returnresolveResponseStatus(responseStatus,request,response,handler,ex); } catch(ExceptionresolveEx){ logger.warn("Handlingof@ResponseStatusresultedinException",resolveEx); } } elseif(ex.getCause()instanceofException){ ex=(Exception)ex.getCause(); //递归 returndoResolveException(request,response,handler,ex); } returnnull; } ResponseStatusExceptionResolver#resolveResponseStatus()-返回异常信息给客户端读取@ResponseStatus注解信息,返回异常内容给客户端
protectedModelAndViewresolveResponseStatus(ResponseStatusresponseStatus,HttpServletRequestrequest, HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{ //状态码 intstatusCode=responseStatus.code().value(); //异常原因描述 Stringreason=responseStatus.reason(); if(this.messageSource!=null){ reason=this.messageSource.getMessage(reason,null,reason,LocaleContextHolder.getLocale()); } //通过response对象直接返回错误信息给客户端 if(!StringUtils.hasLength(reason)){ response.sendError(statusCode); } else{ //通过response对象直接返回错误信息给客户端 response.sendError(statusCode,reason); } returnnewModelAndView(); }源码就不公布了,读者可自行去查询,基本都是调用response的sendError()方法返回错误信息给客户端。本文对其中的异常归下类
请求方式异常 HttpRequestMethodNotSupportedException-服务端不支持相应的请求方法 HttpMediaTypeNotSupportedException/HttpMediaTypeNotAcceptableException-服务端/客户端不支持相应的mediaType,比如application/json MissingPathVariableException-@PathVaribale指定参数请求中不包含 MissingServletRequestParameterException/ServletRequestBindingException-请求参数绑定错误 MethodArgumentNotValidException-@Valid注解指定的参数校验失败 AsyncRequestTimeoutException-异步请求超时 消息内容异常 ConversionNotSupportedException-服务端找寻不到相应的Convert对象来解析javabean TypeMismatchException-设置javabean属性类型出错 HttpMessageNotReadableException/HttpMessageNotWritableException-消息内容不可读/不可写 MissingServletRequestPartException-文件上传类错误,可能请求没有multipart/form-data或者服务不支持文件上传 NoHandlerFoundException-handler处理器没有找到,即可能没有对应的请求处理供响应具体的逻辑本文则不展开了,简述下其中的逻辑:
当处理handlerMethod业务逻辑过程中出现了异常,则此解析器 尝试从handlerMethod所在的class类去找寻是否含有@ExceptionHandler注解的方法 判断@ExceptionHandler指定的exception类与产生的异常一致,一致则执行相应的方法,当有多个@ExceptionHandler(value),则默认采用第一个 当上述在class类找寻不到则尝试判断class类是否含有@ControllerAdvice注解,有则按照上述第一二步的步骤再次找寻@ControllerAdvice指定的类springmvc开放了对异常也可以包装成页面显示的功能,通过本文的简单分析可以帮助博主和读者更好的理解springmvc对异常的处理
本文内容总结:实际应用,HandlerExceptionResolver-异常解析接口,AbstractHandlerExceptionResolver-异常解析抽象基类,1.SimpleMappingExceptionResolver-异常映射实现类,2.ResponseStatusExceptionResolver-响应状态异常解析类,3.DefaultHandlerExceptionResolver-springmvc默认的异常解析处理,4.ExceptionHandlerExceptionResolver-处理handlerMethod对象过程中的异常,小结,
原文链接:https://www.cnblogs.com/question-sky/p/7240628.html