SpringMVC4-组件(四)-HandlerExceptionResolver

《看透springMvc源代码分析与实践》学习笔记
SpringMVC 版本 4.1.5.RELEASE

HandlerExceptionResolver

HandlerExceptionResolver主要用于解析处理过程中产生的异常。

HandlerExceptionResolver 类图

在这里插入图片描述
其中HandlerExceptionResolverComposite作为容器,可以封装别的HandlerExceptionResolver,跟前面的XXXResolverComposite类似。
HandlerExceptionResolver的主要实现类都是继承自AbstractHandlerExceptionResolver,它有五个子类。

  • AnnotationMethodHandlerExceptionResolver已经废弃。
  • AbstractHandlerMethodExceptionResolver和其子类ExceptionHandlerExceptionResolver:一起完成对@ExceptionHandler注释的方法进行异常解析。
  • DefaultHandlerExceptionResolver: 根据各个不同类型的异常,返回不同的异常视图
  • ResponseStatusExceptionResolver: 解析带有@ResponseStatus注释类型的异常
  • SimpleMappingExceptionResolver: 通过配置异常类和view的关系来解析异常

异常解析过程中可以做如下工作:给mav设置相应的内容、设置response相关属性,还可以做一些辅助工作:如记录日志等。

AbstractHandlerMethodExceptionResolver

AbstractHandlerMethodExceptionResolver 是所有解析异常类的父类,里面定义了通用的解析流程,并使用模板方法,子类只需覆盖相应的方法即可。

//org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
    //用于解析哪些handler抛出的异常
    private Set<?> mappedHandlers;
    
     //用于解析哪些handlerClass抛出的异常
    private Class<?>[] mappedHandlerClasses;
    
    private boolean preventResponseCaching = false;
    	
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) {
        //判断是否解析handler
        if (shouldApplyTo(request, handler)) {
            //记录异常日志
            logException(ex, request);
            
            //禁止response缓存: 默认false
            prepareResponse(ex, response);
            
            //模板方法doResolveException,由子类实现
            return doResolveException(request, response, handler, ex);
        } else {
             return null;
         }
    }
    
    protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
        if ( handler!= null) {
            //如果配置了mappedHandlers,则判断是否包含
            if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
                return true;
            }
            //如果配置了mappedHandlerClasses,同样规则判断
            if (this.mappedHandlerClasses != null) {
                for (Class<?> handlerClass : this.mappedHandlerClasses) {
                    if (handlerClass.isInstance(handler)) {
                        return true;
                    }
                }
            }
        }
        
        //如果二者都没有设置值,则恒为true,即解析所有handler
        return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
    }

}

AbstractHandlerMethodExceptionResolver 和 ExceptionHandlerExceptionResolver

AbstractHandlerMethodExceptionResolver

AbstractHandlerMethodExceptionResolver重写了AbstractHandlerExceptionResolver的shouldApplyTodoResolveException方法:

//org.springframework.web.servlet.handler.AbstractHandlerMethodExceptionResolver
@Override
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
    if (handler == null) {
        return super.shouldApplyTo(request, handler);
    }else if (handler instanceof HandlerMethod) {
        //只允许解析 HandlerMethod 抛出的异常
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        handler = handlerMethod.getBean();
        return super.shouldApplyTo(request, handler);
    } else {
       return false;
   }
}

@Override
protected final ModelAndView doResolveException(
        HttpServletRequest request, HttpServletResponse response,
        Object handler, Exception ex) {
    //模板方法,由子类实现
    return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
}

ExceptionHandlerExceptionResolver

ExceptionHandlerExceptionResolver从代码上看,它是一个简化版本的RequestMappingHandlerAdapter

初始化
ExceptionHandlerExceptionResolverRequestMappingHandlerAdapter类似,实现了InitializingBean接口,它的初始化工作在afterPropertiesSet内完成,
而在afterPropertiesSet主要的处理逻辑调用了initExceptionHandlerAdviceCache方法,initExceptionHandlerAdviceCache代码如下:

//org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
		implements ApplicationContextAware, InitializingBean {
    
    private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =
            new ConcurrentHashMap<Class<?>, ExceptionHandlerMethodResolver>(64);
            
    //记录@ControllerAdvice中 ExceptionHandlerMethodResolver 信息
    private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache =
            new LinkedHashMap<ControllerAdviceBean, ExceptionHandlerMethodResolver>();	
    
    private void initExceptionHandlerAdviceCache() {
    	//if部分...略...
    	
    	//寻找所有带有@ControllerAdvice注解的bean
        List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
        Collections.sort(adviceBeans, new OrderComparator());

        for (ControllerAdviceBean adviceBean : adviceBeans) {
            //处理adviceBean类中,带有@ExceptionHandler 注解的方法
            ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());
            if (resolver.hasExceptionMappings()) {
                this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
            }
            
            if (ResponseBodyAdvice.class.isAssignableFrom(adviceBean.getBeanType())) {
                this.responseBodyAdvice.add(adviceBean);
            }
        }
    }
}

ExceptionHandlerMethodResolver
这里重点关注下ExceptionHandlerMethodResolver

//org.springframework.web.method.annotation.ExceptionHandlerMethodResolver{
public class ExceptionHandlerMethodResolver {
    //寻找带有@ExceptionHandler 注解的方法
    public static final MethodFilter EXCEPTION_HANDLER_METHODS = new MethodFilter() {
        @Override
        public boolean matches(Method method) {
            return (AnnotationUtils.findAnnotation(method, ExceptionHandler.class) != null);
        }
    };
    
    //记录异常与处理异常方法映射关系
    private final Map<Class<? extends Throwable>, Method> mappedMethods = new ConcurrentHashMap<Class<? extends Throwable>, Method>(16);
           
    public ExceptionHandlerMethodResolver(Class<?> handlerType) {
        //HandlerMethodSelector.selectMethods :: 寻找带有@ExceptionHandler 注解的方法
        for (Method method : HandlerMethodSelector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
            
            //detectExceptionMappings:: @ExceptionHandler注解的方法中 配置的Throwable类型的值(array)
            for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
                //addExceptionMapping:: 
                addExceptionMapping(exceptionType, method);
            }
        }
    }
    
        private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
            List<Class<? extends Throwable>> result = new ArrayList<Class<? extends Throwable>>();
            //寻找注解中配置的 Throwable
            detectAnnotationExceptionMappings(method, result);
            
            //如果注解中配置的异常为空,则寻找方法入参中的 Throwable 类型参数
            if (result.isEmpty()) {
                for (Class<?> paramType : method.getParameterTypes()) {
                    if (Throwable.class.isAssignableFrom(paramType)) {
                        result.add((Class<? extends Throwable>) paramType);
                    }
                }
            }
            
            //如果result 为空,则抛出异常
            Assert.notEmpty(result, "No exception types mapped to {" + method + "}");
            return result;
        }
        
        //寻找带有@ExceptionHandler注解的方法
        protected void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
            ExceptionHandler annot = AnnotationUtils.findAnnotation(method, ExceptionHandler.class);
            result.addAll(Arrays.asList(annot.value()));
        }
        
        //如果同一个异常有多个method处理,则抛出异常
        private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
            Method oldMethod = this.mappedMethods.put(exceptionType, method);
            if (oldMethod != null && !oldMethod.equals(method)) {
                throw new IllegalStateException(
                        "Ambiguous @ExceptionHandler method mapped for [" + exceptionType + "]: {" +
                        oldMethod + ", " + method + "}.");
            }
        }
}

处理异常
处理异常的逻辑是在doResolveHandlerMethodException中处理的, 它的代码如下:

//org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
    @Override
    protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {
        
        //找到处理异常的方法
        ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
        if (exceptionHandlerMethod == null) {
            return null;
        }
        
        //设置argumentResolvers 和 returnValueHandlers
        exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    
       //执行异常处理方法
      exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception);
    
        if (mavContainer.isRequestHandled()) {
            return new ModelAndView();
        }else {
         ModelAndView mav = new ModelAndView().addAllObjects(mavContainer.getModel());
         mav.setViewName(mavContainer.getViewName());
         if (!mavContainer.isViewReference()) {
             mav.setView((View) mavContainer.getView());
         }
         return mav;
     }
    }
    
    protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
        //找到 执行方法对应的 handlerType
        Class<?> handlerType = (handlerMethod != null ? handlerMethod.getBeanType() : null);

        if (handlerMethod != null) {
            //获取执行方法对应的handlerType ,配置的 ExceptionHandlerMethodResolver信息
            ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
            if (resolver == null) {
                resolver = new ExceptionHandlerMethodResolver(handlerType);
                this.exceptionHandlerCache.put(handlerType, resolver);
            }
            
            //查找 resolver 是否处理该类型的异常,如果有,则返回处理该异常的方法
            Method method = resolver.resolveMethod(exception);
            if (method != null) {
                return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
            }
        }
        
        //从exceptionHandlerAdviceCache缓存信息,处理
        for (Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
            if (entry.getKey().isApplicableToBeanType(handlerType)) {
                ExceptionHandlerMethodResolver resolver = entry.getValue();
                Method method = resolver.resolveMethod(exception);
                if (method != null) {
                    return new ServletInvocableHandlerMethod(entry.getKey().resolveBean(), method);
                }
            }
        }

        return null;
    }

DefaultHandlerExceptionResolver

DefaultHandlerExceptionResolver的解析过程,是根据不同类型的异常,进行不同的处理。

//org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
	@Override
	protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
			Object handler, Exception ex) {

		try {
			if (ex instanceof NoSuchRequestHandlingMethodException) {
				return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response,
						handler);
			}
			else if (ex instanceof HttpRequestMethodNotSupportedException) {
				return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request,
						response, handler);
			}
			//略... 其他类型异常
			else if (ex instanceof NoHandlerFoundException) {
				return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler);
			}
		}
		catch (Exception handlerException) {
			logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
		}
		return null;
	}
	
	//handleXXXException : 处理异常的逻辑是给response设置相应的属性,sendError方法会根据errorCode跳转至web.xml定义的errorPages.
    protected ModelAndView handleNoHandlerFoundException(MissingServletRequestParameterException ex,
            HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {

        response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
        return new ModelAndView();
    }
}

ResponseStatusExceptionResolver

ResponseStatusExceptionResolver用来解析注释了@ResponseStatus的异常。

通常使用自定义异常类,并在该类上增加@ResponseStatus.

//org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver
public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware {
	@Override
	protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
			Object handler, Exception ex) {
		//查看该异常类上是否带有 @ResponseStatus 注解
		ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
		
		if (responseStatus != null) {
	     return resolveResponseStatus(responseStatus, request, response, handler, ex);
	    }
	    return null;
	}

	protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex) throws Exception {
		int statusCode = responseStatus.value().value();
		String reason = responseStatus.reason();
		if (this.messageSource != null) {
			reason = this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale());
		}
		if (!StringUtils.hasLength(reason)) {
			response.sendError(statusCode);
		} else {
            response.sendError(statusCode, reason);
        }
		
		return new ModelAndView();
	}
}

SimpleMappingExceptionResolver

SimpleMappingExceptionResolver需要提前配置异常类和view的对应关系后,才能使用,代码如下:

//org.springframework.web.servlet.handler.SimpleMappingExceptionResolver
public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver {
    public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception";
    
     //用来配置异常类与viewName的对应关系
    private Properties exceptionMappings;

    //配置 不处理的异常类
    private Class<?>[] excludedExceptions;
    
    //当无法从exceptionMappings解析出视图时,使用more视图
    private String defaultErrorView;
  
    //解析出的视图与状态码之间的关系
    private Map<String, Integer> statusCodes = new HashMap<String, Integer>();
    
    //默认状态码
     private Integer defaultStatusCode;
    
    //异常信息保存到Model中的参数名, 默认为 "exception"
    private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE;
    
	@Override
	protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
			Object handler, Exception ex) {
	    //根据异常查找 显示错误的页面
		String viewName = determineViewName(ex, request);
		if (viewName != null) {
		    //从statusCodes中提取配置的viewName的对应的statusCode
			Integer statusCode = determineStatusCode(request, viewName);
			if (statusCode != null) {
				applyStatusCodeIfPossible(request, response, statusCode);
			}
			//返回ModelAndView ,并将ex信息设置到Model的DEFAULT_EXCEPTION_ATTRIBUTE 中
			return getModelAndView(viewName, ex, request);
		} else {
            return null;
        }
	}
	
	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;
                }
            }
        }
        // 寻找配置的 异常-viewName
        if (this.exceptionMappings != null) {
            //根据一定的规则,选择最优匹配viewName
            viewName = findMatchingViewName(this.exceptionMappings, ex);
        }
        //如果没有匹配到,则返回默认的defaultErrorView
        if (viewName == null && this.defaultErrorView != null) {
            viewName = this.defaultErrorView;
        }
        return viewName;
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值