享读SpringMVC源码5-异常处理HandlerExceptionResolver(下)

简介: 享读SpringMVC源码5-异常处理HandlerExceptionResolver(下)
4.3自定义异常处理器


除了对springmvc标准异常的处理。有时候我们需要定义业务异常,来实现对异常更加精细的处理。此时,我们就要考虑自定义异常处理了。


自定义异常处理常用两种方式:


4.3.1实现接口


很容易我们想到的就是实现HandlerExceptionResolver来处理异常


@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        // 自定义异常处理器一般请放在首位
        exceptionResolvers.add(0, new AbstractHandlerExceptionResolver() {
            @Override
            protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
                // 若是自定义的业务异常,那就返回到单页面异常页面
                if (ex instanceof BusinessException) {
                    return new ModelAndView("/business.jsp");
                } else { // 否则统一到统一的错误页面
                    return new ModelAndView("/error.jsp");
                }
            }
        });
    }
}

我们看到返回的试图类型。当我们想直接返回json字符串怎么办??

1.response直接输出json , response.getWriter()相关方法

2.借助MappingJackson2JsonView


4.3.2注解形式

在springboot流行的当下,注解的形式是最常用的自定义异常处理器方式。

@ExceptionHandler注解是spring3.0之后提供的异常处理相关的注解.

@ExceptionHandler 只能标注到一个方法上,被标注的方法就会成为一个异常处理器,处理指定的异常。


他的原理是什么呢?为啥一个方法标注了@ExceptionHandler注解就成为一个异常处理器了呢?


这都与上文提到的HandlerExceptionResolver接口的一个实现类ExceptionHandlerExceptionResolver类有关.


ExceptionHandlerExceptionResolver:

  • 1.类型支持
    通过shouldApplyTo方法我们可以看出此种类型的处理器只能处理HandlerMethod抛出的异常
protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
        if (handler == null) {
            return super.shouldApplyTo(request, handler);
        }
        else if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            handler = handlerMethod.getBean();
            return super.shouldApplyTo(request, handler);
        }
        else {
            return false;
        }
    }
  • 2.异常处理
    通过调用链,此异常处理器的resolveException方法会调用此类的doResolveHandlerMethodException


protected ModelAndView doResolveHandlerMethodException{
        ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
        if (exceptionHandlerMethod == null) {
            return null;
        }
        ...
        exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
}
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(...) {
        //先在本类找可以处理异常的方法
        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);
            }
        }
        //找不到去切面类里去找
        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);
                }
            }
        }
}

可以看到,最终找到一个被@ExceptionHandler注解标示的method 封装成ServletInvocableHandlerMethod 调用执行。

@ExceptionHandler注解标示的method 所在的类,都会被封装成一个ExceptionHandlerMethodResolver。此解析器会根据异常类型,解析出对应的异常处理方法。


寻找过程:

  • 先看本类中有没有可以处理此异常的方法
  • 再从全局异常处理器中寻找。


ExceptionHandlerMethodResolver:异常处理方法解析器。目标:找到一个可以处理异常的方法。

这里有两种方式:一是从本Controller中寻找,另一个是从ControllerAdviceBean切面里找。

1.局部生效异常处理: 我们可以使用@ExceptionHandler注解在本类中标识一个方法,使其可以处理异常。他的初始化在此类异常第一次被处理时。封装一个此类对应的ExceptionHandlerMethodResolver,放入到ExceptionHandlerExceptionResolver.exceptionHandlerCache缓存中

2.全局异常处理:我们可以用@ControllerAdvice注解标示一个类是全局的异常处理。在此类中使用@ExceptionHandler定义各种各样的异常处理方法。此类会被封装成一个ExceptionHandlerMethodResolver,放入到ExceptionHandlerExceptionResolver.exceptionHandlerAdviceCache缓存中。他的初始化是在

ExceptionHandlerExceptionResolverbean初始化过程中afterPropertiesSet中初始化的

@Override
    public void afterPropertiesSet() {
        // 初始化@ControllerAdvice标识的全局异常处理
        initExceptionHandlerAdviceCache();
    }


4.3.3小结

@ExceptionHandler注解标识的方法所在的类,会被封装成一个ExceptionHandlerMethodResolver .ExceptionHandlerExceptionResolver 异常处理器,会找到一个ExceptionHandlerMethodResolverExceptionHandlerMethodResolver根据对应的异常类型找到一个处理方法进行处理

优先级顺序为:

  1. @Controller + @ExceptionHandler优先级最高
  2. @ControllerAdvice + @ExceptionHandler次之
  3. HandlerExceptionResolver最后(一般是DefaultHandlerExceptionResolver)


5.总结


推荐使用注解定义全局异常的形式处理异常,即@ControllerAdvice + @ExceptionHandler组合


如果本文任何错误,请批评指教,不胜感激 !

如果文章哪些点不懂,可以联系我交流学习!


相关文章
|
4月前
|
容器
SpringMVC常见组件之HandlerExceptionResolver分析-2
SpringMVC常见组件之HandlerExceptionResolver分析-2
36 0
|
2月前
|
Java 应用服务中间件 Spring
Spring5源码(50)-SpringMVC源码阅读环境搭建
Spring5源码(50)-SpringMVC源码阅读环境搭建
41 0
|
2月前
|
Java 应用服务中间件 数据库连接
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
Spring5源码(51)-Servlet知识点回顾以及SpringMVC分析入口
36 0
|
4月前
|
设计模式 前端开发 Java
[Spring ~源码] Spring的run方法以及SpringMVC执行流程
[Spring ~源码] Spring的run方法以及SpringMVC执行流程
|
9月前
|
存储 前端开发 搜索推荐
(八)Spring源码解析:Spring MVC
(八)Spring源码解析:Spring MVC
54 1
|
4月前
|
SQL JSON 前端开发
【源码免费下载】SpringBoot整合Spring+SpringMVC+MyBatisPlus案例:图书管理系统
【源码免费下载】SpringBoot整合Spring+SpringMVC+MyBatisPlus案例:图书管理系统
64 0
|
4月前
|
JSON 前端开发 Java
SpringMVC常见组件之HandlerExceptionResolver分析-1
SpringMVC常见组件之HandlerExceptionResolver分析-1
50 0
|
前端开发 Java Spring
源码浅析SpringMVC请求的流转过程
Spring MVC框架使用了其”模型-视图-控制器”( Model-View-Controller )架构方式,用于开发灵活且松散耦合的 Web 应用程序。我们都使用过SpringMVC来处理信息,并渲染视图到Browser。但需要注意的是,在现在的架构中,大都采用了前后端分离的情况,而我们在使用SpringMVC的时候,只需要关注M(Model),C(Controller)这两个部分,而视图渲染的部分则交给了前端。
286 0
源码浅析SpringMVC请求的流转过程
|
Java Spring 容器
享读SpringMVC源码5-异常处理HandlerExceptionResolver(上)
享读SpringMVC源码5-异常处理HandlerExceptionResolver(上)
享读SpringMVC源码5-异常处理HandlerExceptionResolver(上)
|
4月前
|
设计模式 前端开发 JavaScript
Spring MVC(一)【什么是Spring MVC】
Spring MVC(一)【什么是Spring MVC】