目录
AbstractHandlerExceptionResolver
DefaultHandlerExceptionResolver
ResponseStatusExceptionResolver
SimpleMappingExceptionResolver
AbstractHandlerMethodExceptionResolver
ExceptionHandlerExceptionResolver
在Spring MVC中,HandlerExceptionResolver组件用于处理在请求处理过程中抛出的异常,然后返回ModelAndView,接口定义如下
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
SpringMVC默认定义的处理器为下边三种,挨个读
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
AbstractHandlerExceptionResolver
所有 HandlerExceptionResolver实现类的父类,定义通用模板
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
@Nullable
private Set<?> mappedHandlers;
@Nullable
private Class<?>[] mappedHandlerClasses;
@Override
@Nullable
public ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
// shouldApplyTo()方法判断本处理器是否能处理该类型的handler,如果处理不了 返回null
if (shouldApplyTo(request, handler)) {
prepareResponse(ex, response);
// 开始处理异常,抽象方法子类实现
ModelAndView result = doResolveException(request, response, handler, ex);
return result;
}
else {
return null;
}
}
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
// 本抽象类定义了两个集合,mappedHandlers和mappedHandlerClasses
// 如果定义的HandlerExceptionResolver设置了这两个集合,那么根据集合中的handler来匹配
// 没定义的话 默认是都返回true,表示可以处理该handler
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);
}
protected abstract ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
DefaultHandlerExceptionResolver
给了默认的异常处理,就是针对不同异常设置不同的HTTP响应码,返回new ModelAndView()
public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
@Override
@Nullable
protected ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
try {
if (ex instanceof HttpRequestMethodNotSupportedException) {
return handleHttpRequestMethodNotSupported(
(HttpRequestMethodNotSupportedException) ex, request, response, handler);
}
else if (ex instanceof HttpMediaTypeNotSupportedException) {
return handleHttpMediaTypeNotSupported(
(HttpMediaTypeNotSupportedException) ex, request, response, handler);
}
else if (ex instanceof HttpMediaTypeNotAcceptableException) {
return handleHttpMediaTypeNotAcceptable(
(HttpMediaTypeNotAcceptableException) ex, request, response, handler);
}
else if (ex instanceof MissingPathVariableException) {
return handleMissingPathVariable(
(MissingPathVariableException) ex, request, response, handler);
}
//。。。。。。等等各种异常
}
catch (Exception handlerEx) {
if (logger.isWarnEnabled()) {
logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx);
}
}
return null;
}
}
protected ModelAndView handleMissingPathVariable(MissingPathVariableException ex,
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
// 设置500 响应码
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage());
return new ModelAndView();
}
ResponseStatusExceptionResolver
用来解析@ResponseStatus标注的Exception类,并把注解定义的状态码返回给客户端
public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware {
@Override
@Nullable
protected ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
try {
if (ex instanceof ResponseStatusException) {
return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler);
}
// 如果controller报出的异常被@ResponseStatus注解,将repsonse中的响应码和错误信息换成注解上的
ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
if (status != null) {
return resolveResponseStatus(status, request, response, handler, ex);
}
if (ex.getCause() instanceof Exception) {
return doResolveException(request, response, handler, (Exception) ex.getCause());
}
}
catch (Exception resolveEx) {}
return null;
}
protected ModelAndView applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response)
throws IOException {
if (!StringUtils.hasLength(reason)) {
response.sendError(statusCode);
}
else {
String resolvedReason = (this.messageSource != null ?
this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) :
reason);
response.sendError(statusCode, resolvedReason);
}
return new ModelAndView();
}
}
SimpleMappingExceptionResolver
spring自带的简单实现,主要功能是根据controller爆出的Exception类型,匹配对应的错误页面
xml配置如下,例如果抛出IOException就跳转到error1.jsp界面
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置的错误页面 -->
<property name="defaultErrorView" value="error"></property>
<!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
<property name="exceptionAttribute" value="ex"></property>
<!--定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,将不同的异常映射到不同的页面上-->
<property name="exceptionMappings">
<props>
<prop key="IOException">error1</prop>
<prop key="TestException">error2</prop>
</props>
</property>
<!-- 本解析器不支持处理的异常类型 -->
<property name="excludedExceptions">
<set>
<value>com.lyq.framework.common.exception.BusinessException</value>
<value>java.sql.SQLException</value>
</set>
</property>
</bean>
public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver {
@Nullable
private Properties exceptionMappings;// 异常类型和错误界面的对应关系
@Nullable
private Class<?>[] excludedExceptions;// 不处理的异常类型
@Nullable
private String defaultErrorView;// 默认的错误界面
@Nullable
private Integer defaultStatusCode;// 默认错误码
private Map<String, Integer> statusCodes = new HashMap<>();// 错误界面和错误码的对应关系
protected ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
// 根据异常类型选择错误界面
String viewName = determineViewName(ex, request);
if (viewName != null) {
// 如果定义了错误页面指定的状态码或是设置了默认的状态码,设置之
Integer statusCode = determineStatusCode(request, viewName);
if (statusCode != null) {
applyStatusCodeIfPossible(request, response, statusCode);
}
return getModelAndView(viewName, ex, request);
}
else {
return null;
}
}
}
determineViewName
1、先判断该异常类型本解析类是否支持处理,不支持返回null
2、 循环配置的对应关系,获取最匹配的错误界面
3、如果没找到使用默认的错误界面
protected String determineViewName(Exception ex, HttpServletRequest request) {
String viewName = null;
if (this.excludedExceptions != null) {
for (Class<?> excludedEx : this.excludedExceptions) {
if (excludedEx.equals(ex.getClass())) {
return null;
}
}
}
// Check for specific exception mappings.
if (this.exceptionMappings != null) {
viewName = findMatchingViewName(this.exceptionMappings, ex);
}
// Return default error view else, if defined.
if (viewName == null && this.defaultErrorView != null) {
if (logger.isDebugEnabled()) {
logger.debug("Resolving to default view '" + this.defaultErrorView + "'");
}
viewName = this.defaultErrorView;
}
return viewName;
}
protected String findMatchingViewName(Properties exceptionMappings, Exception ex) {
String viewName = null;
String dominantMapping = null;
int deepest = Integer.MAX_VALUE;
for (Enumeration<?> names = exceptionMappings.propertyNames(); names.hasMoreElements();) {
String exceptionMapping = (String) names.nextElement();
// 递归该异常的继承关系,找到匹配的那个异常类型,记录下递归了多少次
int depth = getDepth(exceptionMapping, ex);
if (depth >= 0 && (depth < deepest || (depth == deepest &&
dominantMapping != null && exceptionMapping.length() > dominantMapping.length()))) {
// 递归次数越少,越匹配
deepest = depth;
dominantMapping = exceptionMapping;
viewName = exceptionMappings.getProperty(exceptionMapping);
}
}
return viewName;
}
private int getDepth(String exceptionMapping, Class<?> exceptionClass, int depth) {
if (exceptionClass.getName().contains(exceptionMapping)) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
if (exceptionClass == Throwable.class) {
return -1;
}
return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1);
}
AbstractHandlerMethodExceptionResolver
ExceptionHandlerExceptionResolver用来处理@ExceptionHandler注解定义的异常处理方法,AbstractHandlerMethodExceptionResolver是它的父类,重写了shouldApplyTo方法,只支持HandlerMethod类型的handler,即@RequestMapping定义的处理器
public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {
@Override
protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) {
if (handler == null) {
return super.shouldApplyTo(request, null);
}
else if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
handler = handlerMethod.getBean();
return super.shouldApplyTo(request, handler);
}
else {
return false;
}
}
@Override
@Nullable
protected final ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
}
@Nullable
protected abstract ModelAndView doResolveHandlerMethodException(
HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception ex);
}
ExceptionHandlerExceptionResolver
用@ExceptionHandler注解的方法进行异常处理
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
implements ApplicationContextAware, InitializingBean {
}
有InitializingBean 接口,来看afterPropertiesSet方法
afterPropertiesSet
获取@ControllerAdvice定义的全局ExceptionHandler方法
@Override
public void afterPropertiesSet() {
// 获取ControllerAdvice类中被@ExceptdionHandler注解的方法
// 还有获取响应处理ResponseBodyAdvice类
initExceptionHandlerAdviceCache();
// 调用ExceptdionHandler注解方法时使用的入参转换器和出参解析器
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
private void initExceptionHandlerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
// 获取容器中所有ControllerAdvice,
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
// 获取ControllerAdvice类中被@ExceptionHandler注解的方法
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
if (resolver.hasExceptionMappings()) {
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
}
}
}
//
public ExceptionHandlerMethodResolver(Class<?> handlerType) {
for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
addExceptionMapping(exceptionType, method);
}
}
}
doResolveHandlerMethodException
获取匹配的ExceptionHandler方法,然后调用该方法,返回MAV
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
// 获取跟异常匹配的@ExceptionHandler方法
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
// 设置方法的入参转换和出参解析处理器
if (this.argumentResolvers != null) {
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
try {
// 调用方法
Throwable cause = exception.getCause();
if (cause != null) {
// Expose cause as provided argument as well
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
}
else {
// Otherwise, just the given exception as-is
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
}
}
catch (Throwable invocationEx) {
return null;
}
// 返回MAV
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
}
else {
ModelMap model = mavContainer.getModel();
HttpStatus status = mavContainer.getStatus();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
mav.setViewName(mavContainer.getViewName());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
}
获取Exception匹配的@ExceptionHandler方法,优先使用Controller中定义的,其次使用全局ControllerAdvice中的
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
@Nullable HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = null;
// 如果handlerMethod所在的controller中有匹配的@ExceptionHandler方法,优先使用
if (handlerMethod != null) {
handlerType = handlerMethod.getBeanType();
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
if (Proxy.isProxyClass(handlerType)) {
handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
}
}
// 再从全局ControllerAdvice中查找匹配的异常处理方法
for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
ControllerAdviceBean advice = entry.getKey();
if (advice.isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
}
}
}
return null;
}