spring @ExceptionHandler 异常处理3

版权声明:欢迎交流、沟通 QQ:983433479 微信:wj983433479 ;努力学习~赚钱养家不是梦。 https://blog.csdn.net/u012881904/article/details/80636680

spring @ExceptionHandler 异常处理3

闲聊

在经过前面的两篇博客的简单闲聊的基础上,我们已经基本明确啦,如何掌握和深入了解spring异常处理体系的原理基本的途径,以及通过哪几个类可以简单的了解,下面从源码的角度了解其中的几个简单的类!最复杂的处理@ExpectionHandler等下一篇在继续闲聊。

HandlerExceptionResolver的继承体系图

这里写图片描述

HandlerExceptionResolver

下面的这些话是spring源码中呈现的,有点类似解析HandlerMethod执行处理的过程一样的
接口的实现者可以解析在处理程序映射或执行期间(handler mapping or execution)抛出的异常的,通常情况下为错误视图。实现者通常在应用程序上下文中注册为bean。

返回代表特定错误页面的{@link ModelAndView},返回的ModelAndView 可能为空{@linkplain ModelAndView#isEmpty() empty},表示已经解析完成,但是没有呈现的视图,但是也可以设置视图的状态码;返回null证明当前这个解析器无法解析这个异常。

public interface HandlerExceptionResolver {
    ModelAndView resolveException(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);

}

AbstractHandlerExceptionResolver

AbstractHandlerExceptionResolver class 是这个HandlerResolver实行类,主要是实现了Order排序,和简单的条件过滤,这里的条件过滤主要是支持两种类型的条件过滤,第一个是对于特定的Handler的实例才支持过滤setMappedHandlers,第二个就是支持特定的子类的实例相对于Class而言 #setMappedHandlerClasses
Supports mapped {@linkplain #setMappedHandlers handlers} and {@linkplain #setMappedHandlerClasses handler classes} that the resolver should be applied to and implements the {@link Ordered} interface.
这里写图片描述
成员变量:

    /**
     * 指定这个异常解析器应该适用的set of handlers,当前的处理程序支持的所有的集合
     */
    private Set<?> mappedHandlers;

    /**
     * 指定这个异常解析器应该适用的set of classes,
     * 可以指定指定的Handler处理器实现的接口或者handler的超类等等,或者本身也是可以的
     * 和上面的实现类的实例相辅相成
     */
    private Class<?>[] mappedHandlerClasses;
    /**
     * 通过设置相应的来防止响应被缓存
     */
    protected void preventCaching(HttpServletResponse response) {
        response.addHeader(HEADER_CACHE_CONTROL, "no-store");
    }
    //留给子类实现的模板方法
    protected abstract ModelAndView doResolveException(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);

这里讲的的几句话就实现了对于特定的实例mappdeHandler和特定Class 的Handler的过滤!使用 IsInstance 方法进行过滤操作处理,可以是实现对于局部的异常的处理,不过大多数情况下,异常处理对于所有的类都是有效的!所以这里基本上不会使用!还是简单的提及一下。
这里写图片描述

子实现类

还记得在第二篇的时候讲过,一些实现类通过spring 的命名空间或者通过spring javaConfig进行了配置处理,所以在DispatchServlet处理的时候可以发现他们,这里再次贴一下这张图。前面的三个是spring提供的,最后一个是我自己简单的写了一个实现了HandlerResolver
这里写图片描述

DefaultHandlerExceptionResolver

解析标准的Spring MVC异常并将它们转换为相应的HTTP状态代码,也就是说,如果HandlerMapping参数无法解析、或者其他的无法解析都是由于这个参数造成转换成相应的 response.sendError(HttpServletResponse.SC_NOT_FOUND);这样的异常对应了web.xml中配置的异常信息对应的视图的信息。这里的处理都是返回错误的视图哦!不过这里只是返回视图的信息,如果对于restful的调用那么就不是很友好啦!

 /
 * @see org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
 * @see #handleNoSuchRequestHandlingMethod
 * @see #handleHttpRequestMethodNotSupported
 * @see #handleHttpMediaTypeNotSupported
 * @see #handleMissingServletRequestParameter
 * @see #handleServletRequestBindingException
 * @see #handleTypeMismatch
 * @see #handleHttpMessageNotReadable
 * @see #handleHttpMessageNotWritable
 * @see #handleMethodArgumentNotValidException
 * @see #handleMissingServletRequestParameter
 * @see #handleMissingServletRequestPartException
 * @see #handleBindException
 */
 protected ModelAndView doResolveException(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

        try {
            if (ex instanceof HttpRequestMethodNotSupportedException) {
                return handleHttpRequestMethodNotSupported(
                        (HttpRequestMethodNotSupportedException) ex, request, response, handler);
            }

        else if (ex instanceof AsyncRequestTimeoutException) {
                return handleAsyncRequestTimeoutException(
                        (AsyncRequestTimeoutException) ex, request, response, handler);
            }
            ........
        catch (Exception handlerException) {

        }
        return null;

protected ModelAndView handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
            HttpServletRequest request, HttpServletResponse response, Object handler)  {

        pageNotFoundLogger.warn(ex.getMessage());
        String[] supportedMethods = ex.getSupportedMethods();
        if (supportedMethods != null) {
            response.setHeader("Allow", 
            StringUtils.arrayToDelimitedString(supportedMethods, ", "));
        }
        response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());
        return new ModelAndView();
    }
解决返回视图的问题,spring提供了一个返回ResponseEntity的解决方案 ResponseEntityExceptionHandler

ResponseEntity 和我们经常看到的@ResponseBody效果差不多!只是可以通过设置视图,通过HttpMessageConverter 进行转换哦!
这里写图片描述

ResponseEntityExceptionHandler

ResponseEntityExceptionHandler 的处理方式是通过@ExpectionHandler哦,配合@ControlerAdvice进行的。下面介绍的非常的详细吧!这个就是上面的DefaultHandlerExceptionResolver的翻版,我们需要继承这个类,然后在类上面使用@ControllerAdvice 而且必须在全局注册 ExceptionHandlerExceptionResolver,这个类需要了解的地方太多啦!慢慢的接触。和这篇文章类似,只是我们不需要这么麻烦的去处理异常信息Spring Boot @ControllerAdvice 处理全局异常,返回固定格式Json

/**
 * A convenient base class for {@link ControllerAdvice @ControllerAdvice} classes
 * that wish to provide centralized exception handling across all
 * {@code @RequestMapping} methods through {@code @ExceptionHandler} methods.
 *
 * <p>This base class provides an {@code @ExceptionHandler} method for handling
 * internal Spring MVC exceptions. This method returns a {@code ResponseEntity}
 * for writing to the response with a {@link HttpMessageConverter message converter},
 * in contrast to
 * {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
 * DefaultHandlerExceptionResolver} which returns a
 * {@link org.springframework.web.servlet.ModelAndView ModelAndView}.
 *
 * <p>If there is no need to write error content to the response body, or when
 * using view resolution (e.g., via {@code ContentNegotiatingViewResolver}),
 * then {@code DefaultHandlerExceptionResolver} is good enough.
 *
 * <p>Note that in order for an {@code @ControllerAdvice} subclass to be
 * detected, {@link ExceptionHandlerExceptionResolver} must be configured.
 *
 * @author Rossen Stoyanchev
 * @since 3.2
 * @see #handleException(Exception, WebRequest)
 * @see org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
 */
 @ExceptionHandler({
            org.springframework.web.servlet.mvc.multiaction
            .NoSuchRequestHandlingMethodException.class,
            HttpRequestMethodNotSupportedException.class,
            HttpMediaTypeNotSupportedException.class,
            HttpMediaTypeNotAcceptableException.class,
            MissingPathVariableException.class,
            MissingServletRequestParameterException.class,
            ServletRequestBindingException.class,
            ConversionNotSupportedException.class,
            TypeMismatchException.class,
            HttpMessageNotReadableException.class,
            HttpMessageNotWritableException.class,
            MethodArgumentNotValidException.class,
            MissingServletRequestPartException.class,
            BindException.class,
            NoHandlerFoundException.class,
            AsyncRequestTimeoutException.class
        })
 throws Exception {
        HttpHeaders headers = new HttpHeaders();
.NoSuchRequestHandlingMethodException) {
            HttpStatus status = HttpStatus.NOT_FOUND;
            return handleNoSuchRequestHandlingMethod((org.springframework.web
            .servlet.mvc.multiaction
            .NoSuchRequestHandlingMethodException) ex, headers, status, request);
        }

        else {
            // Unknown exception, typically a wrapper with a common MVC exception as cause
            // (since @ExceptionHandler type declarations also match first-level causes):
            // We only deal with top-level MVC exceptions here, so let's rethrow the given
            // exception for further processing through the HandlerExceptionResolver chain.
            throw ex;
        }
    }

ResponseStatusExceptionResolver

ResponseStatusExceptionResolver说实话,我还真的没有使用过这样的需求!老是的对于这种使用需求真实没得!特别是对于现在的Resful的开发来说;下面这里有个参考的实例,哈哈,感觉就是和之前Default…..一样的处理逻辑!只是这里有一个区别,上面的是spring默认定义的错误信息,这里是我们自己定义,然后放置在异常处理类上面的注解的处理!
ResponseStatus注解处理异常
原理很简单:就是解析到异常上面是否有特定的注解!遍历其所有的父类直到找到了第一个,然后获取到注解的信息通过response.sendError(statusCode, resolvedReason);处理即可完事;

public class ResponseStatusExceptionResolver extends 
    AbstractHandlerExceptionResolver implements MessageSourceAware {

    private MessageSource messageSource;


    @Override
    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }


    @Override
    protected ModelAndView doResolveException(
            HttpServletRequest request, HttpServletResponse response
            , Object handler, Exception ex) {
        /**
         * 1、遍历其祖宗十八代 然后找到ResponseStatus注解 
         * 2、response.sendError(statusCode, resolvedReason);处理这里还处理了国际化的逻辑哦!
         */
        ResponseStatus status = AnnotatedElementUtils
        .findMergedAnnotation(ex.getClass(), ResponseStatus.class);
        if (status != null) {
            try {
                return resolveResponseStatus(status, request, response, handler, ex);
            }
            catch (Exception resolveEx) {
                logger.warn("ResponseStatus handling resulted in exception", resolveEx);
            }
        }
        else if (ex.getCause() instanceof Exception) {
            ex = (Exception) ex.getCause();
            return doResolveException(request, response, handler, ex);
        }
        return null;
    }

    /**
     * Template method that handles the {@link ResponseStatus @ResponseStatus} annotation.
     * <p>The default implementation sends a response error using
     * {@link HttpServletResponse#sendError(int)} or
     * {@link HttpServletResponse#sendError(int, String)} if the annotation has a
     * {@linkplain ResponseStatus#reason() reason} and then returns an empty ModelAndView.
     * @param responseStatus the annotation
     * @param request current HTTP request
     * @param response current HTTP response
     * @param handler the executed handler, or {@code null} if none chosen at the
     * time of the exception, e.g. if multipart resolution failed
     * @param ex the exception
     * @return an empty ModelAndView, i.e. exception resolved 返回空的ModelAndView,说明已经解决啦!
     */
    protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus
    , HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) throws Exception {

        int statusCode = responseStatus.code().value();
        String reason = responseStatus.reason();
        if (!StringUtils.hasLength(reason)) {
            response.sendError(statusCode);
        }
        else {
            String resolvedReason = (this.messageSource != null ?
 null, reason, LocaleContextHolder.getLocale()) :
                    reason);
            response.sendError(statusCode, resolvedReason);
        }
        return new ModelAndView();
    }

}

SimpleMappingExceptionResolver

还有一个异常处理类,允许 mapping exception class names to view names,这里的view类类似ModeAndView中view的名称,就是将特定的异常转换到特定的视图层去处理,错误视图类似于错误页面JSP,但可以与任何类型的异常一起使用,包括任何已检查的错误视图,以及针对特定处理程序的精细映射。在Default…上面处理智能处理到特定的错误码,对于特定的异常返回到特定的视图,这个估计非常的好使用哦!反正就是配置一个错误异常和view之间映射的匹配。
SimpleMappingExceptionResolver的使用,哈哈其实就是一样的,就是多了一些特性而已,一个是针对错误码!通过web.xml映射到错误的界面,一个是通过view,然后通过spring的视图解析去处理。ModelAndView mv = new ModelAndView(viewName);重点就是找到错误的挑转的viewname

总结

今天早上,起来的有点晚!早餐吃完过后才写了第一篇博客,然后睡了一会,比较困;爬起来又去开始生活的节奏,弄弄生活,品尝品尝美食、有了生活才有工作啊!不过学习也是免不了,虽然这些代码都看过,但是真正的书写的过程中还是有一些的疑问,不断的了解不断的充实!加油。

猜你喜欢

转载自blog.csdn.net/u012881904/article/details/80636680
今日推荐