Bootstrap

SpringMVC源码笔记(二) HandlerExceptionResolver

目录

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;
	}

;