SpringMVC源码情操陶冶-AbstractHandlerExceptionResolver
springmvc支持服务端在处理业务逻辑过程中出现异常的时候可以配置相应的ModelAndView对象返回给客户端,本文介绍springmvc默认的几种HandlerExceptionResolver类
实际应用
springmvc的xml配置化-Exception配置
<bean id="exceptionHandler" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!--设置默认返回viewName,通常与freemarker引擎搭配使用-->
<property name="defaultErrorView" value="error/defaultError" />
<!--设置默认返回response status-->
<property name="defaultStatusCode" value="500" />
<!--配置相应的异常类与viewName的映射-->
<property name="exceptionMappings">
<props>
<prop key="SessionTimeoutException">redirect:../login.html</prop>
<prop key="AuthenticationException">error/403</prop>
</props>
</property>
</bean>
以上的配置会对出SessionTimeoutException异常则跳转至login页面,对AuthenticationException异常则跳转至403页面,对其他的异常则默认跳转至defaultError页面呈现并返回500的错误码
HandlerExceptionResolver-异常解析接口
接口内只有一个方法resolveException()
,通过解析异常查询配置以得到符合条件的ModelAndView对象
/**
* Try to resolve the given exception that got thrown during handler execution,
* returning a {@link ModelAndView} that represents a specific error page if appropriate.
* <p>The returned {@code ModelAndView} may be {@linkplain ModelAndView#isEmpty() empty}
* to indicate that the exception has been resolved successfully but that no view
* should be rendered, for instance by setting a status code.
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler, or {@code null} if none chosen at the
* time of the exception (for example, if multipart resolution failed)
* @param ex the exception that got thrown during handler execution
* @return a corresponding {@code ModelAndView} to forward to, or {@code null}
* for default processing
*/
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
AbstractHandlerExceptionResolver-异常解析抽象基类
所有的spring内置异常解析类都继承于此,直奔主题看resolveException()方法
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
//判断是否需要解析
if (shouldApplyTo(request, handler)) {
//此处一般是判断内部属性preventResponseCaching是否为true,是则设置响应包头cache-control:no-store
prepareResponse(ex, response);
//使用模板方法doResolveException()方法供子类实现
ModelAndView result = doResolveException(request, response, handler, ex);
if (result != null) {
//日志打印一发
logException(ex, request);
}
return result;
}
else {
return null;
}
}
附带着分析下shouldApplyTo()方法
/**
**可以配置mappedHandlers和mappedHandlerClasses属性来特定匹配
**默认情况下两者都为空则直接返回true,表明对所有的handler都进行异常解析
*/
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
//此处的handler一般为bean对象
if (handler != null) {
if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
return true;
}
if (this.mappedHandlerClasses != null) {
for (Class<?> handlerClass : this.mappedHandlerClasses) {
if (handlerClass.isInstance(handler)) {
return true;
}
}
}
}
// Else only apply if there are no explicit handler mappings.
return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
}
1. SimpleMappingExceptionResolver-异常映射实现类
比较简单的实现类,可以配绑定viewName和exception以完成简单的异常映射视图页面
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
// Expose ModelAndView for chosen error view.
String viewName = determineViewName(ex, request);
if (viewName != null) {
//如果配置了statusCodes属性,则对此异常的状态码进行设置
Integer statusCode = determineStatusCode(request, viewName);
if (statusCode != null) {
applyStatusCodeIfPossible(request, response, statusCode);
}
//创建ModelAndView对象
return getModelAndView(viewName, ex, request);
}
else {
return null;
}
}
针对以上的源码我们分两步去简单分析下
SimpleMappingExceptionResolver#determineViewName()-找寻viewName
protected String determineViewName(Exception ex, HttpServletRequest request) {
String viewName = null;
//判断异常是否属于excludeExceptions集合内,是则直接返回null
if (this.excludedExceptions != null) {
for (Class<?> excludedEx : this.excludedExceptions) {
if (excludedEx.equals(ex.getClass())) {
return null;
}
}
}
// Check for specific exception mappings.
// 从exceptionMappings集合内根据exception获取到相应的viewName
if (this.exceptionMappings != null) {
viewName = findMatchingViewName(this.exceptionMappings, ex);
}
//当exceptionMappings集合内不存在指定的exception但是默认视图指定则直接返回默认视图
if (viewName == null && this.defaultErrorView != null) {
viewName = this.defaultErrorView;
}
return viewName;
}
excludedExceptions集合可以过滤指定的exception,对其不进行解析直接返回null。可配置
exceptionMappings绑定了exception与viewName的关系,如果在其集合内没找到相应的viewName,但是defaultErrorView属性指定,则会直接返回defaultErrorView对应的视图
SimpleMappingExceptionResolver#getModelAndView()-创建ModelAndView对象
protected ModelAndView getModelAndView(String viewName, Exception ex) {
ModelAndView mv = new ModelAndView(viewName);
//exceptionAttribute默认为exception
if (this.exceptionAttribute != null) {
//将exception信息添加到model中
mv.addObject(this.exceptionAttribute, ex);
}
return mv;
}
主要就是将exceptionAttribute对应的参数值默认为
exception
属性添加到视图对象的model中
2. ResponseStatusExceptionResolver-响应状态异常解析类
主要是解析带有@ResponseStatus
的异常类,将其中的异常信息描述直接返回给客户端
@Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
//获取相应类上的注解@ResponseStatus
ResponseStatus responseStatus = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
if (responseStatus != null) {
try {
return resolveResponseStatus(responseStatus, request, response, handler, ex);
}
catch (Exception resolveEx) {
logger.warn("Handling of @ResponseStatus resulted in Exception", resolveEx);
}
}
else if (ex.getCause() instanceof Exception) {
ex = (Exception) ex.getCause();
//递归
return doResolveException(request, response, handler, ex);
}
return null;
}
ResponseStatusExceptionResolver#resolveResponseStatus()-返回异常信息给客户端
读取@ResponseStatus
注解信息,返回异常内容给客户端
protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws Exception {
//状态码
int statusCode = responseStatus.code().value();
//异常原因描述
String reason = 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);
}
return new ModelAndView();
}
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对异常的处理
SpringMVC源码情操陶冶-AbstractHandlerExceptionResolver的更多相关文章
- SpringMVC源码情操陶冶-DispatcherServlet简析(二)
承接前文SpringMVC源码情操陶冶-DispatcherServlet类简析(一),主要讲述初始化的操作,本文将简单介绍springmvc如何处理请求 DispatcherServlet#doDi ...
- SpringMVC源码情操陶冶-FreeMarker之web配置
前言:本文不讲解FreeMarkerView视图的相关配置,其配置基本由FreeMarkerViewResolver实现,具体可参考>>>SpringMVC源码情操陶冶-ViewRe ...
- SpringMVC源码情操陶冶-DispatcherServlet
本文对springmvc核心类DispatcherServlet作下简单的向导,方便博主与读者查阅 DispatcherServlet-继承关系 分析DispatcherServlet的继承关系以及主 ...
- SpringMVC源码情操陶冶-AnnotationDrivenBeanDefinitionParser注解解析器
mvc:annotation-driven节点的解析器,是springmvc的核心解析器 官方注释 Open Declaration org.springframework.web.servlet.c ...
- SpringMVC源码情操陶冶-DispatcherServlet父类简析
阅读源码有助于陶冶情操,本文对springmvc作个简单的向导 springmvc-web.xml配置 <servlet> <servlet-name>dispatch< ...
- SpringMVC源码情操陶冶-DispatcherServlet类简析(一)
阅读源码有利于陶冶情操,此文承接前文SpringMVC源码情操陶冶-DispatcherServlet父类简析 注意:springmvc初始化其他内容,其对应的配置文件已被加载至beanFactory ...
- SpringMVC源码情操陶冶-HandlerAdapter适配器简析
springmvc中对业务的具体处理是通过HandlerAdapter适配器操作的 HandlerAdapter接口方法 列表如下 /** * Given a handler instance, re ...
- SpringMVC源码情操陶冶-AbstractHandlerMethodMapping
承接前文SpringMVC源码情操陶冶-AbstractHandlerMapping,本文将介绍如何注册HandlerMethod对象作为handler 类结构瞧一瞧 public abstract ...
- SpringMVC源码情操陶冶-AbstractUrlHandlerMapping
承接前文SpringMVC源码情操陶冶-AbstractHandlerMapping,前文主要讲解了如何获取handler处理对象,本文将针对beanName注册为handler对象作下解析 Abst ...
随机推荐
- Log POST Data in Nginx
先编译Nginx的echo module: ./configure --add-module=./echo-nginx-module-0.54 make make install 可以在github上 ...
- JS/jQ常用宽高及应用
关于js的宽高,随便一搜就是一大堆.这个一大堆对我来说可不是什么好事,看的头都大了.所以今天就总结了一些比较会常用的,并说明一下应用场景. 先来扯一下documentElement和body的微妙关系 ...
- MVC 树节点Table格式授权
这几夜心里颇不平静, 奈何 JS水平有限,前台效果耗时四天,后台传值一天,直至昨夜丑时测试初步完成,其实就是一个给tree来授权,网上开源的插件很多,如treejs.easyui 等等,只是这里授权稍 ...
- JAVA下JSON的类型输出及使用
JSON类型的输出: import java.util.ArrayList; import java.util.HashMap; import net.sf.json.JSONArray; impor ...
- 处理jQuery append加入的元素 绑定事件无效的方法
通过jquery append(或者before.after,一样)新添加进网页的元素,常用的事件不能触发,比如:append了id 为 abc 的元素,那么 $(#abc).click(functi ...
- Docker Machine 详解
笔者在<Docker Machine 简介>一文中简单介绍了 Docker Machine 及其基本用法,但是忽略的细节实在是太多了.比如 Docker 与 Docker Machine ...
- AI相关 TensorFlow -卷积神经网络 踩坑日记之一
上次写完粗浅的BP算法 介绍 本来应该继续把 卷积神经网络算法写一下的 但是最近一直在踩 TensorFlow的坑.所以就先跳过算法介绍直接来应用场景,原谅我吧. TensorFlow 介绍 TF是g ...
- 用jquery循环获得所有input标签里的value值
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- sublime text 3双击选择无法选择$符号
创建/Data/Packages/User/PHP.sublime-settings文件,内容为"word_separators": "./\\()\"'-:, ...
- asp.net使用qq邮箱发送邮件
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Ne ...