SpringMVC源码---HandlerExceptionResolver

HandlerExceptionResolver用于解析请求过程中所产生的异常,继承结构图:

简介:

    HandlerExceptionResolver主要实现都继承与抽象类的AbstractHandlerExecutionResolver,它有五个子类,其中AnnotationMethodHandlerExceptionResolver已经被弃用。剩余的

  1. AbstractHandlerMethodExceptionResolver和其子类ExceptionHandlerExcpetionResolver一起完成@ExceptionHandler注释的方法进行异常解析的功能
  2. DefaultHandlerExceptionResolver按照不同类型分别对异常进行解析
  3. ResponseStatusExceptionResolver解析@ResponseStatus注释类型的异常
  4. SimpleMappingExceptionResolver:通过配置的异常类和view的对应关系解析异常

HandlerExecutionResolverComposite作为一个容器使用,里面封装了别的Resolver。

异常解析的主要过程:给ModelAndView设置相应的内容,设置response的相关属性。当然还可能有一些辅助功能,如记录日志等,在自定义的ExceptionHandler里还可以做更多的事。

 

1.AbstractHandlerExecutionResolver

       它是所有直接解析异常类的父类,里面有通用的解析流程,并使用了模板方法doResolveException(实际处理异常的方法),看源码:

    @Nullable
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
        //判断当前ExceptionResolver是否可以解析所传入处理器抛出的异常,如果不可以返回null,交给下一个解析
        if (this.shouldApplyTo(request, handler)) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
            }
            //设置response
            this.prepareResponse(ex, response);
            //调用这个实际解析异常,这是模板方法交给子类实现
            ModelAndView result = this.doResolveException(request, response, handler, ex);
            if (result != null) {
                this.logException(ex, request);
            }

            return result;
        } else {
            return null;
        }
    }

    protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler{
        //这里的mappedHandlers用于配置处理器的集合,mappedHandlerClasses用于配置处理器类型的集合,如果设置了这两个属性的一个,那么ExceptionResolver就只能解析所设置的处理器抛出的异常    
        if (handler != null) {
            if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
                return true;
            }

            if (this.mappedHandlerClasses != null) {
                Class[] var3 = this.mappedHandlerClasses;
                int var4 = var3.length;

                for(int var5 = 0; var5 < var4; ++var5) {
                    Class<?> handlerClass = var3[var5];
                    if (handlerClass.isInstance(handler)) {
                        return true;
                    }
                }
            }
        }

        return this.mappedHandlers == null && this.mappedHandlerClasses == null;
    }

2.ExceptionHandlerExceptionResolver

       这个类继承AbstractHandlerMethodExceptionResolver,后者又继承于AbstractHandlerMethodExceptionResolver重写了shouldApplyTo方法,并且在处理请求的doResolverException方法中将实际处理请求的过程交给了模板方法doResolveHanlerMethodException.

  2.1AbstractHandlerMethodExceptionResolver相当于一个适配器,源码如下:

  public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {
    public AbstractHandlerMethodExceptionResolver() {
    }

    protected boolean shouldApplyTo(HttpServletRequest request, @Nullable Object handler{        
        //如果handler为空,交给父类处理
        if (handler == null) {
            return super.shouldApplyTo(request, (Object)null);
        //判断了handler是否属于HandlerMethod,并且将处理器设置为其所在的类,然后交给父类决断
        } else if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod)handler;
            handler = handlerMethod.getBean();
            return super.shouldApplyTo(request, handler);
        //如果handler既不为空,并且也不属于handlerMethod直接返回false
        } else {
            return false;
        }
    }

    @Nullable
    protected final ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
        return this.doResolveHandlerMethodException(request, response, (HandlerMethod)handler, ex);
    }
    //留给子类的模板方法
    @Nullable
    protected abstract ModelAndView doResolveHandlerMethodException(HttpServletRequest var1, HttpServletResponse var2, @Nullable HandlerMethod var3, Exception var4);
}

  2.2 ExceptionHandlerExceptionResolver

            这个其实也是一个简化版的RequestMappingHandlerAdapter,执行也是使用的ServletInvocableHandlerMethod,首先更具handlerMethod和exception将其创建出来(在处理器类中找出所有注释了@ExceptionHandler的防范,然后根据配置中的异常和需要解析的异常进行匹配),然后设置了argumentResolvers和returnValueHandlers,接着调用其invokeAndHandle方法执行处理,最后封装好结果ModelAndView返回。源码如下:

    @Nullable
    protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
        //找到处理异常的方法
        ServletInvocableHandlerMethod exceptionHandlerMethod = this.getExceptionHandlerMethod(handlerMethod, exception);
        if (exceptionHandlerMethod == null) {
            return null;
        } else {
             //设置argumentResolvers和returnValueHandlers
            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 {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);
                }

                Throwable cause = exception.getCause();
                if (cause != null) {
                    //执行解析异常
                    exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, new Object[]{exception, cause, handlerMethod});
                } else {
                    exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, new Object[]{exception, handlerMethod});
                }
            } catch (Throwable var12) {
                if (var12 != exception && this.logger.isWarnEnabled()) {
                    this.logger.warn("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, var12);
                }

                return null;
            }

            if (mavContainer.isRequestHandled()) {
                return new ModelAndView();
            } else {
                //封装ModelAndView 
                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;
            }
        }
    }

 

3.DefaultHandlerExceptionResolver

      这个解析过程是根据异常类型不同,使用不同方法进行处理。解析方法代码:

@Nullable
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
        try {
            if (ex instanceof HttpRequestMethodNotSupportedException) {
                return this.handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException)ex, request, response, handler);
            }

            if (ex instanceof HttpMediaTypeNotSupportedException) {
                return this.handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException)ex, request, response, handler);
            }

            if (ex instanceof HttpMediaTypeNotAcceptableException) {
                return this.handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException)ex, request, response, handler);
            }

            if (ex instanceof MissingPathVariableException) {
                return this.handleMissingPathVariable((MissingPathVariableException)ex, request, response, handler);
            }

            if (ex instanceof MissingServletRequestParameterException) {
                return this.handleMissingServletRequestParameter((MissingServletRequestParameterException)ex, request, response, handler);
            }

            if (ex instanceof ServletRequestBindingException) {
                return this.handleServletRequestBindingException((ServletRequestBindingException)ex, request, response, handler);
            }

            if (ex instanceof ConversionNotSupportedException) {
                return this.handleConversionNotSupported((ConversionNotSupportedException)ex, request, response, handler);
            }

            if (ex instanceof TypeMismatchException) {
                return this.handleTypeMismatch((TypeMismatchException)ex, request, response, handler);
            }

            if (ex instanceof HttpMessageNotReadableException) {
                return this.handleHttpMessageNotReadable((HttpMessageNotReadableException)ex, request, response, handler);
            }

            if (ex instanceof HttpMessageNotWritableException) {
                return this.handleHttpMessageNotWritable((HttpMessageNotWritableException)ex, request, response, handler);
            }

            if (ex instanceof MethodArgumentNotValidException) {
                return this.handleMethodArgumentNotValidException((MethodArgumentNotValidException)ex, request, response, handler);
            }

            if (ex instanceof MissingServletRequestPartException) {
                return this.handleMissingServletRequestPartException((MissingServletRequestPartException)ex, request, response, handler);
            }

            if (ex instanceof BindException) {
                return this.handleBindException((BindException)ex, request, response, handler);
            }

            if (ex instanceof NoHandlerFoundException) {
                return this.handleNoHandlerFoundException((NoHandlerFoundException)ex, request, response, handler);
            }

            if (ex instanceof AsyncRequestTimeoutException) {
                return this.handleAsyncRequestTimeoutException((AsyncRequestTimeoutException)ex, request, response, handler);
            }
        } catch (Exception var6) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in exception", var6);
            }
        }

        return null;
    }

调用的方法一个例子源码:

   protected ModelAndView handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
        pageNotFoundLogger.warn(ex.getMessage());
        String[] supportedMethods = ex.getSupportedMethods();
        if (supportedMethods != null) {
            response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
        }

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

从这里可以简单看出来,注释给repsonse设置相应属性和返回一个空ModelAndView,sendError可以返回错误码,通过在页面可以获取到响应信息。sendError和setStatus方法的区别是前者会返回web.xml中定义的错误页面,后者只是设置了status而不会返回响应错误页面,其他处理方法类似。

 

4.ResponseStatusExceptionResolver

      他是用来解析@ResponseStatus的异常(如自定义了注释@ResponseStatus的异常),源码:

    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
        try {
            if (ex instanceof ResponseStatusException) {
                return this.resolveResponseStatusException((ResponseStatusException)ex, request, response, handler);
            }
            //找到@ResponseStatus注释
            ResponseStatus status = (ResponseStatus)AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
            if (status != null) {
                //然后调用解析方法
                return this.resolveResponseStatus(status, request, response, handler, ex);
            }

            if (ex.getCause() instanceof Exception) {
                ex = (Exception)ex.getCause();
                return this.doResolveException(request, response, handler, ex);
            }
        } catch (Exception var6) {
            this.logger.warn("ResponseStatus handling resulted in exception", var6);
        }

        return null;
    }

    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 this.applyStatusAndReason(statusCode, reason, response);
    }

 

5.SimpleMappingExceptionResolver

     这个需要提前配置异常类和view的对应关系然后才能使用,关键方法源码如下:

 

     protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
        //首先根据异常朝赵显示错误的逻辑视图
        String viewName = this.determineViewName(ex, request);
        if (viewName != null) {
            //检查是否配置了所找到的viewName对应的statusCode
            Integer statusCode = this.determineStatusCode(request, viewName);
            if (statusCode != null) {
                //设置response
                this.applyStatusCodeIfPossible(request, response, statusCode);
            }
            //将异常和解析出的viewName封装成ModelAndView并返回
            return this.getModelAndView(viewName, ex, request);
        } else {
            return null;
        }
    }

    @Nullable
    protected String determineViewName(Exception ex, HttpServletRequest request) {
        String viewName = null;
        //如果异常在设置的excludeExceptions中包含则返回null
        if (this.excludedExceptions != null) {
            Class[] var4 = this.excludedExceptions;
            int var5 = var4.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                Class<?> excludedEx = var4[var6];
                if (excludedEx.equals(ex.getClass())) {
                    return null;
                }
            }
        }
        //调用findMatchingViewName方法实际查找
        if (this.exceptionMappings != null) {
            viewName = this.findMatchingViewName(this.exceptionMappings, ex);
        }
        //如果没有找到,并且有默认视图在采用默认视图
        if (viewName == null && this.defaultErrorView != null) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Resolving to default view '" + this.defaultErrorView + "' for exception of type [" + ex.getClass().getName() + "]");
            }

            viewName = this.defaultErrorView;
        }

        return viewName;
    }

这个类还有相关的属性解析:

public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver {
    public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception";
    @Nullable
    private Properties exceptionMappings; //用于配置异常(字符串)和viewName的对应关系,异常类可以是异常的一部分,还可以是异常父类的一部分
    @Nullable
    private Class<?>[] excludedExceptions;//用于配置不处理的异常
    @Nullable
    private String defaultErrorView;//用于配置当无法从exceptionMapping中解析出视图使用的默认视图
    @Nullable
    private Integer defaultStatusCode;//用于配置statusCodes中没有配置相应的viewName时使用的默认statusCode
    private Map<String, Integer> statusCodes = new HashMap();//用于配置解除viewName和statusCode对应的关系
    @Nullable
    private String exceptionAttribute = "exception";//用于配置异常在Model中保存的参数名,默认为exception,如果为null,异常将不保存到Model中
....
}

       

总结:

     mvc:annotation-driven会自动将ExceptionHandlerExceptionREsolver、DefaultHandlerExceptionResolver和ResponseStatusExceptinResolver配置到SpringMVC中,如果SimpleMappingExceptionResolver要使用需要自行配置。

另外这个异常只能处理请求处理过程抛出的异常,异常处理本身抛出的异常和视图解析过程中抛出的异常时不能做到的。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring MVC是一个基于Java的Web框架,它提供了很多简化Web开发的功能。其中一个常用的功能是处理HTTP请求和响应。而Jackson-databind是一个Java库,用于将Java对象与JSON(JavaScript Object Notation)之间进行相互转换。 在Spring MVC中,我们可以使用Jackson-databind来处理请求和响应中的JSON数据。具体来说,我们可以使用Jackson-databind的ObjectMapper类来将JSON数据转换为Java对象,或将Java对象转换为JSON数据。 例如,我们可以在Spring MVC中定义一个Controller,用于处理来自客户端的HTTP请求,并返回一个Java对象,然后使用Jackson-databind将该Java对象转换为JSON数据,以便客户端可以使用该数据进行相应的操作。 示例代码如下: ```java @Controller public class MyController { @GetMapping("/myendpoint") @ResponseBody public MyObject handleRequest() { // 处理请求,返回一个 MyObject 对象 MyObject obj = new MyObject(); obj.setProperty1("value1"); obj.setProperty2("value2"); return obj; } // 使用 Jackson-databind 将 MyObject 对象转换为 JSON 数据 @GetMapping(value = "/myendpoint", produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public ResponseEntity<String> handleRequest() throws JsonProcessingException { MyObject obj = new MyObject(); obj.setProperty1("value1"); obj.setProperty2("value2"); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(obj); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return new ResponseEntity<String>(json, headers, HttpStatus.OK); } } ``` 在上面的示例中,我们定义了一个Spring MVC的Controller,该Controller处理来自客户端的HTTP请求,并返回一个MyObject对象。随后,我们使用Jackson-databind将该对象转换为JSON数据,并将其返回给客户端。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值