springMVC源码简读——2.4 HandlerExceptionResolver

    xiaoxiao2023-11-18  165

    概述

    处理器异常解析器接口,将处理执行时发生的异常,解析成对应的 ModelAndView 结果

    顶级接口

    public interface HandlerExceptionResolver { /** * 解析异常,转换成对应的 ModelAndView 结果 */ @Nullable ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex); }

    类图

    初始化

    springboot配置下的初始化,代码在DispatcherServlet(为什么在这之前几篇已经说了)的initHandlerExceptionResolvers

    // DispatcherServlet.java // 初始化异常处理 private void initHandlerExceptionResolvers(ApplicationContext context) { this.handlerExceptionResolvers = null; // 假如进行自动扫描 if (this.detectAllHandlerExceptionResolvers) { // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts. // 扫描符合要求 (类型HandlerExceptionResolver)的类 // 保存进异常处理集合后,进行排序 Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values()); // We keep HandlerExceptionResolvers in sorted order. AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers); } } else { try { // 从容器中获得名字为HANDLER_EXCEPTION_RESOLVER_BEAN_NAME的类,并设置到 // handlerExceptionResolvers异常处理集合中 HandlerExceptionResolver her = context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class); this.handlerExceptionResolvers = Collections.singletonList(her); } catch (NoSuchBeanDefinitionException ex) { // Ignore, no HandlerExceptionResolver is fine too. } } // Ensure we have at least some HandlerExceptionResolvers, by registering // default HandlerExceptionResolvers if no other resolvers are found. // 如果未获得到,则获得默认配置的 HandlerExceptionResolver 类 if (this.handlerExceptionResolvers == null) { this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }

    主要三种方式;

    优先进行自动扫描假如非自动扫描则从容器中获得指定对象假如上面两者都不是的则创建默认异常处理

    实现类

    HandlerExceptionResolverComposite

    实现 HandlerExceptionResolver、Ordered 接口,复合的 HandlerExceptionResolver 实现类

    构造函数&参数

    // HandlerExceptionResolverComposite.java /** * 处理器集合 */ @Nullable private List<HandlerExceptionResolver> resolvers; /** * 优先级策略 */ private int order = Ordered.LOWEST_PRECEDENCE;

    resolveException

    遍历数组,逐个处理异常

    // WebMvcConfigurationSupport.java @Override @Nullable public ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { // 遍历 HandlerExceptionResolver 数组,逐个处理异常 ex ,如果成功,则返回 ModelAndView 对象 if (this.resolvers != null) { for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) { ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex); // 假如成功了就返回 if (mav != null) { return mav; } } } return null; }

    AbstractHandlerExceptionResolver

    实现 HandlerExceptionResolver、Ordered 接口,HandlerExceptionResolver 抽象类,作为所有 HandlerExceptionResolver 实现类的基类

    构造函数

    // AbstractHandlerExceptionResolver.java /** * 优先级策略 */ private int order = Ordered.LOWEST_PRECEDENCE; /** * 处理器集合 */ @Nullable private Set<?> mappedHandlers; /** * 匹配的处理器类型的数组 */ @Nullable private Class<?>[] mappedHandlerClasses; /** * 防止响应缓存 */ private boolean preventResponseCaching = false;

    shouldApplyTo

    判断当前 HandlerExceptionResolver 是否能应用到传入的 handler 处理器

    // AbstractHandlerExceptionResolver.java protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) { if (handler != null) { // 如果 mappedHandlers 包含 handler 对象,则返回 true if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) { return true; } // 如果 mappedHandlerClasses 包含 handler 的类型,则返回 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. // 如果 mappedHandlers 和 mappedHandlerClasses 都为空,说明直接匹配 return (this.mappedHandlers == null && this.mappedHandlerClasses == null); }

    看逻辑request并没有使用,目前不清楚为啥传递过来

    prepareResponse

    阻止响应缓存

    // AbstractHandlerExceptionResolver.java protected void prepareResponse(Exception ex, HttpServletResponse response) { // 根据配置是否阻止响应 if (this.preventResponseCaching) { preventCaching(response); } } // 在头信息设置 no-store protected void preventCaching(HttpServletResponse response) { response.addHeader(HEADER_CACHE_CONTROL, "no-store"); }

    resolveException

    处理异常

    // AbstractHandlerExceptionResolver.java protected abstract ModelAndView doResolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);

    AbstractHandlerExceptionResolver 并没有实现逻辑,主要交给子类实现

    AbstractHandlerMethodExceptionResolver

    继承 AbstractHandlerExceptionResolver 抽象类,基于 handler 类型为 HandlerMethod 的 HandlerExceptionResolver 抽象类

    shouldApplyTo

    重写shouldApplyTo

    // AbstractHandlerMethodExceptionResolver.java protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler) { // 如果handler为空则直接使用父类方法 if (handler == null) { return super.shouldApplyTo(request, null); } // 若handler为HandlerMethod则进行处理后调用方法 else if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; handler = handlerMethod.getBean(); return super.shouldApplyTo(request, handler); } else { return false; } }

    重写了父类的校验内容,主要针对HandlerMethod的处理,获取真正的handler再进行判断。

    doResolveException

    重写 doResolveException

    // AbstractHandlerMethodExceptionResolver.java @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 配置 HandlerMethod 的 HandlerExceptionResolver 实现类,使用 @ExceptionHandler 注解来实现过异常的处理。

    构造方法

    // ExceptionHandlerExceptionResolver.java public ExceptionHandlerExceptionResolver() { StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316 // 初始化消息转换器 this.messageConverters = new ArrayList<>(); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(stringHttpMessageConverter); try { this.messageConverters.add(new SourceHttpMessageConverter<>()); } catch (Error err) { // Ignore when no TransformerFactory implementation is available } this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); }

    初始化了各种转换器

    afterPropertiesSet

    进一步初始化 ExceptionHandlerExceptionResolver ,InitializingBean接口中的方法,有兴趣可以看看这个接口内容。

    // ExceptionHandlerExceptionResolver.java @Override public void afterPropertiesSet() { // Do this first, it may add ResponseBodyAdvice beans // 初始化 exceptionHandlerAdviceCache、responseBodyAdvice两个参数的值 initExceptionHandlerAdviceCache(); // 初始化argumentResolvers 获得默认的getDefaultArgumentResolvers参数解析并设置到初始化argumentResolvers中 if (this.argumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } // returnValueHandlers 获得默认的getDefaultReturnValueHandlers返回解析并设置到初始化argumentResolvers中 if (this.returnValueHandlers == null) { List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers(); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); } }

    初始化了一些解析器

    initExceptionHandlerAdviceCache

    在afterPropertiesSet的第一步就是这个方法,主要是初始化 exceptionHandlerAdviceCache、responseBodyAdvice。

    // ExceptionHandlerExceptionResolver.java private void initExceptionHandlerAdviceCache() { if (getApplicationContext() == null) { return; } // 描 @ControllerAdvice 注解的 Bean 们,并将进行排序 List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); AnnotationAwareOrderComparator.sort(adviceBeans); // 遍历扫描出来的结果 for (ControllerAdviceBean adviceBean : adviceBeans) { Class<?> beanType = adviceBean.getBeanType(); if (beanType == null) { throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean); } // 扫描该ControllerAdvice对应的类型 ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType); // 有 @ExceptionHandler 注解,则添加到 exceptionHandlerAdviceCache 中 if (resolver.hasExceptionMappings()) { this.exceptionHandlerAdviceCache.put(adviceBean, resolver); } // 如果该 beanType 类型是 ResponseBodyAdvice 子类,则添加到 responseBodyAdvice 中 if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) { this.responseBodyAdvice.add(adviceBean); } } if (logger.isDebugEnabled()) { int handlerSize = this.exceptionHandlerAdviceCache.size(); int adviceSize = this.responseBodyAdvice.size(); if (handlerSize == 0 && adviceSize == 0) { logger.debug("ControllerAdvice beans: none"); } else { logger.debug("ControllerAdvice beans: " + handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice"); } } }

    用过异常的统一处理方法可以发现

    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

    spring将所有的ControllerAdviceBean会被分类添加到this.exceptionHandlerAdviceCache和this.responseBodyAdvice中

    其他的方法

    getDefaultArgumentResolvers,获得默认的 HandlerMethodArgumentResolver 数组getDefaultReturnValueHandlers,获得默认的 HandlerMethodReturnValueHandler 数组

    ExceptionHandlerMethodResolver

    ExceptionHandlerExceptionResolver.initExceptionHandlerAdviceCache 中创建了这个类。他的作用就是注解了 @ExceptionHandler 的方法的解析器

    构造方法

    // ExceptionHandlerMethodResolver.java /** * 映射的方法 */ private final Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16); /** * 匹配的方法异常 */ private final Map<Class<? extends Throwable>, Method> exceptionLookupCache = new ConcurrentReferenceHashMap<>(16); /** * A constructor that finds {@link ExceptionHandler} methods in the given type. * @param handlerType the type to introspect */ public ExceptionHandlerMethodResolver(Class<?> handlerType) { // 遍历注解的方法 for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) { // 遍历处理的异常集合 for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) { // 添加到异常映射中 addExceptionMapping(exceptionType, method); } } }

    hasExceptionMappings

    判断 mappedMethods 非空

    // ExceptionHandlerMethodResolver.java /** * Whether the contained type has any exception mappings. * 简单的判断是否有异常映射 */ public boolean hasExceptionMappings() { return !this.mappedMethods.isEmpty(); }

    resolveMethod

    解析异常对应的方法

    // ExceptionHandlerMethodResolver.java public Method resolveMethod(Exception exception) { return resolveMethodByThrowable(exception); } @Nullable public Method resolveMethodByThrowable(Throwable exception) { // 首先获得异常对应的方法 Method method = resolveMethodByExceptionType(exception.getClass()); if (method == null) { // 假如获取不到则使用异常的cause再次获取 Throwable cause = exception.getCause(); if (cause != null) { method = resolveMethodByExceptionType(cause.getClass()); } } return method; }

    主要是获得被我们注解的方法,处理异常的逻辑就在我们自定义的方法中

    getExceptionHandlerMethod

    回到ExceptionHandlerMethodResolver我们在getExceptionHandlerMethod这个方法中获得异常对应的 ServletInvocableHandlerMethod 对象

    // ExceptionHandlerMethodResolver.java @Nullable protected ServletInvocableHandlerMethod getExceptionHandlerMethod( @Nullable HandlerMethod handlerMethod, Exception exception) { // 处理器类型 Class<?> handlerType = null; // 首先,如果 handlerMethod 非空,则先获得 Controller 对应的 @ExceptionHandler 处理器对应的方法 if (handlerMethod != null) { // Local exception handler methods on the controller class itself. // To be invoked through the proxy, even in case of an interface-based proxy. // 获得 handlerType 以及对应的 ExceptionHandlerMethodResolver 对象 handlerType = handlerMethod.getBeanType(); ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType); if (resolver == null) { resolver = new ExceptionHandlerMethodResolver(handlerType); this.exceptionHandlerCache.put(handlerType, resolver); } // 获得异常对应的 Method 方法,假如不为空则返回ServletInvocableHandlerMethod对象 Method method = resolver.resolveMethod(exception); if (method != null) { return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method); } // For advice applicability check below (involving base packages, assignable types // and annotation presence), use target class instead of interface-based proxy. // 假如是代理类的话,则获得 handlerType 的原始类。因为,此处有可能是代理对象 if (Proxy.isProxyClass(handlerType)) { handlerType = AopUtils.getTargetClass(handlerMethod.getBean()); } } // 其次,使用 ControllerAdvice 对应的 @ExceptionHandler 处理器对应的方法 for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) { ControllerAdviceBean advice = entry.getKey(); // 如果 ControllerAdvice 支持当前的 handlerType if (advice.isApplicableToBeanType(handlerType)) { // 获得 handlerType 对应的 ExceptionHandlerMethodResolver 对象 ExceptionHandlerMethodResolver resolver = entry.getValue(); // 获得异常对应的 Method 方法 假如不为空则返回ServletInvocableHandlerMethod对象 Method method = resolver.resolveMethod(exception); if (method != null) { return new ServletInvocableHandlerMethod(advice.resolveBean(), method); } } } return null; }

    可以看到调用了获得方法的代码

    Method method = resolver.resolveMethod(exception); // 最后返回需要的对象 return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);

    doResolveHandlerMethodException

    进行异常处理,这回算是实现了

    // ExceptionHandlerMethodResolver.java @Override @Nullable protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) { // 获得异常对应的 ServletInvocableHandlerMethod 对象 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 和 ModelAndViewContainer ServletWebRequest webRequest = new ServletWebRequest(request, response); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); try { if (logger.isDebugEnabled()) { logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod); } // 执行 ServletInvocableHandlerMethod 的调用 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) { // Any other than the original exception is unintended here, // probably an accident (e.g. failed assertion or the like). if (invocationEx != exception && logger.isWarnEnabled()) { logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx); } // Continue with default processing of the original exception... return null; } // 如果已经处理,则返回一个空的视图 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; } }

    主要是获得异常处理方法的包装ServletInvocableHandlerMethod对象。然后将参数和各种解析器设置进对象中执行,自定义的异常处理方法Method

    ResponseStatusExceptionResolver

    基于 @ResponseStatus 提供错误响应的 HandlerExceptionResolver 实现类

    构造方法

    // ResponseStatusExceptionResolver.java // 解析消息的策略接口 @Nullable private MessageSource messageSource;

    doResolveException

    执行异常处理

    // ResponseStatusExceptionResolver.java protected ModelAndView doResolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { try { // 假如一次属于ResponseStatusException则使用resolveResponseStatusException解析 if (ex instanceof ResponseStatusException) { return resolveResponseStatusException((ResponseStatusException) ex, request, response, handler); } // 假如存在ResponseStatus注解,则使用resolveResponseStatus解析 ResponseStatus status = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class); if (status != null) { return resolveResponseStatus(status, request, response, handler, ex); } // 假如前两项都不行,则使用异常的cause再次执行 if (ex.getCause() instanceof Exception) { return doResolveException(request, response, handler, (Exception) ex.getCause()); } } catch (Exception resolveEx) { if (logger.isWarnEnabled()) { logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", resolveEx); } } return null; } protected ModelAndView resolveResponseStatusException(ResponseStatusException ex, HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws Exception { int statusCode = ex.getStatus().value(); String reason = ex.getReason(); return applyStatusAndReason(statusCode, reason, response); } protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception { int statusCode = responseStatus.code().value(); String reason = responseStatus.reason(); return applyStatusAndReason(statusCode, reason, response); }

    可以看到最后使用的方法都是applyStatusAndReason

    applyStatusAndReason

    再其doResolveException方法中有多种判断,但是最终使用的方法逻辑都是applyStatusAndReason,其作用是设置错误响应

    // ResponseStatusExceptionResolver.java protected ModelAndView applyStatusAndReason(int statusCode, @Nullable String reason, HttpServletResponse response) throws IOException { // 假如没有错误提示,则设置状态码 if (!StringUtils.hasLength(reason)) { response.sendError(statusCode); } else { // 进一步解析错误提示,如果有 messageSource 的情况下,然后将错误内容设置到response中 String resolvedReason = (this.messageSource != null ? this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale()) : reason); response.sendError(statusCode, resolvedReason); } // 返回一个空视图 return new ModelAndView(); }

    DefaultHandlerExceptionResolver

    继承 AbstractHandlerExceptionResolver 抽象类,默认 HandlerExceptionResolver 实现类,针对各种异常,设置错误响应,内置了一些错误响应策略

    // DefaultHandlerExceptionResolver.java @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); } else if (ex instanceof MissingServletRequestParameterException) { return handleMissingServletRequestParameter( (MissingServletRequestParameterException) ex, request, response, handler); } else if (ex instanceof ServletRequestBindingException) { return handleServletRequestBindingException( (ServletRequestBindingException) ex, request, response, handler); } else if (ex instanceof ConversionNotSupportedException) { return handleConversionNotSupported( (ConversionNotSupportedException) ex, request, response, handler); } else if (ex instanceof TypeMismatchException) { return handleTypeMismatch( (TypeMismatchException) ex, request, response, handler); } else if (ex instanceof HttpMessageNotReadableException) { return handleHttpMessageNotReadable( (HttpMessageNotReadableException) ex, request, response, handler); } else if (ex instanceof HttpMessageNotWritableException) { return handleHttpMessageNotWritable( (HttpMessageNotWritableException) ex, request, response, handler); } else if (ex instanceof MethodArgumentNotValidException) { return handleMethodArgumentNotValidException( (MethodArgumentNotValidException) ex, request, response, handler); } else if (ex instanceof MissingServletRequestPartException) { return handleMissingServletRequestPartException( (MissingServletRequestPartException) ex, request, response, handler); } else if (ex instanceof BindException) { return handleBindException((BindException) ex, request, response, handler); } else if (ex instanceof NoHandlerFoundException) { return handleNoHandlerFoundException( (NoHandlerFoundException) ex, request, response, handler); } else if (ex instanceof AsyncRequestTimeoutException) { return handleAsyncRequestTimeoutException( (AsyncRequestTimeoutException) ex, request, response, handler); } } catch (Exception handlerEx) { if (logger.isWarnEnabled()) { logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx); } } return null; }

    SimpleMappingExceptionResolver

    是 Spring MVC 提供的一个简易匹配的异常处理方式

    xml中的配置

    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!-- 默认异常视图 --> <property name="defaultErrorView" value="error" /> <!-- 视图中获取exception信息变量名 --> <property name="exceptionAttribute" value="ex" /> <!-- 异常同视图映射关系 --> <property name="exceptionMappings"> <props> <prop key="com.daify.sprinlearn.exception.DaiException">dai</prop> </props> </property> </bean>

    doResolveException

    进行异常处理

    // SimpleMappingExceptionResolver.java protected ModelAndView doResolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { // Expose ModelAndView for chosen error view. // 获得异常对应视图 String viewName = determineViewName(ex, request); if (viewName != null) { // Apply HTTP status code for error views, if specified. // Only apply it if we're processing a top-level request. // 获得视图对应状态码 Integer statusCode = determineStatusCode(request, viewName); if (statusCode != null) { // 设置状态码 applyStatusCodeIfPossible(request, response, statusCode); } // 返回一个视图对象 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; } } } // 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; // 遍历 exceptionMappings 数组,寻找最匹配的视图名 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); } } // 返回视图名 if (viewName != null && logger.isDebugEnabled()) { logger.debug("Resolving to view '" + viewName + "' based on mapping [" + dominantMapping + "]"); } return viewName; }

    这样在配置中配置的对应视图,通过findMatchingViewName(this.exceptionMappings, ex);就可以获取对应的值了

    总结

    组件的使用,在DispatcherServlet.java的异常流程是这样的。

    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception { // Success and error responses may use different content types // 移除 PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE 属性 request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); // Check registered HandlerExceptionResolvers... // 遍历 HandlerExceptionResolver 数组,解析异常,生成 ModelAndView 对象 ModelAndView exMv = null; if (this.handlerExceptionResolvers != null) { // 遍历 HandlerExceptionResolver 数组 for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) { // 解析异常,生成 ModelAndView 对象 exMv = resolver.resolveException(request, response, handler, ex); // 生成成功,结束循环 if (exMv != null) { break; } } } if (exMv != null) { // ModelAndView 对象为空,则返回 null if (exMv.isEmpty()) { request.setAttribute(EXCEPTION_ATTRIBUTE, ex); return null; } // We might still need view name translation for a plain error model... // 设置默认视图 if (!exMv.hasView()) { String defaultViewName = getDefaultViewName(request); if (defaultViewName != null) { exMv.setViewName(defaultViewName); } } // 打印日志 if (logger.isTraceEnabled()) { logger.trace("Using resolved error view: " + exMv, ex); } if (logger.isDebugEnabled()) { logger.debug("Using resolved error view: " + exMv); } // 设置请求中的错误消息属性 WebUtils.exposeErrorRequestAttributes(request, ex, getServletName()); // 渲染 ModelAndView return exMv; } // 未生成 ModelAndView 对象,则抛出异常 throw ex; }

    其实整个流程很简单,无非是拿到解析器,然后得到返回的视图进行返回。

    最新回复(0)