SpringMVC源码情操陶冶-AbstractHandlerExceptionResolver
本文内容纲要:
-实际应用
-HandlerExceptionResolver-异常解析接口
-AbstractHandlerExceptionResolver-异常解析抽象基类
-1.SimpleMappingExceptionResolver-异常映射实现类
-2.ResponseStatusExceptionResolver-响应状态异常解析类
-3.DefaultHandlerExceptionResolver-springmvc默认的异常解析处理
-4.ExceptionHandlerExceptionResolver-处理handlerMethod对象过程中的异常
-小结
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的错误码
HandlerExceptionResolver-异常解析接口
接口内只有一个方法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);
AbstractHandlerExceptionResolver-异常解析抽象基类
所有的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);
}
1.SimpleMappingExceptionResolver-异常映射实现类
比较简单的实现类,可以配绑定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中
2.ResponseStatusExceptionResolver-响应状态异常解析类
主要是解析带有@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();
}
3.DefaultHandlerExceptionResolver-springmvc默认的异常解析处理
源码就不公布了,读者可自行去查询,基本都是调用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处理器没有找到,即可能没有对应的请求处理供响应
4.ExceptionHandlerExceptionResolver-处理handlerMethod对象过程中的异常
具体的逻辑本文则不展开了,简述下其中的逻辑:
当处理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