SpringMVC异常处理源码分析

发布时间:2023-03-21 14:15:46 作者:iii
来源:亿速云 阅读:159

SpringMVC异常处理源码分析

引言

在Web应用开发中,异常处理是一个非常重要的环节。SpringMVC作为Java Web开发的主流框架之一,提供了强大的异常处理机制。本文将深入分析SpringMVC异常处理的源码,帮助读者理解其工作原理和实现细节。

1. SpringMVC异常处理概述

SpringMVC的异常处理机制主要通过HandlerExceptionResolver接口来实现。该接口定义了处理控制器方法执行过程中抛出的异常的方法。SpringMVC提供了多种实现类,如DefaultHandlerExceptionResolverExceptionHandlerExceptionResolver等,用于处理不同类型的异常。

2. HandlerExceptionResolver接口

HandlerExceptionResolver接口是SpringMVC异常处理的核心接口,定义如下:

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

该接口只有一个方法resolveException,用于解析并处理异常。参数handler是抛出异常的处理器(通常是Controller方法),ex是抛出的异常。

3. 默认的异常处理器

SpringMVC默认提供了多个HandlerExceptionResolver的实现类,其中最常用的是DefaultHandlerExceptionResolverExceptionHandlerExceptionResolver

3.1 DefaultHandlerExceptionResolver

DefaultHandlerExceptionResolver是SpringMVC默认的异常处理器,用于处理常见的HTTP错误,如404、500等。其源码如下:

public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
    @Override
    protected ModelAndView doResolveException(
        HttpServletRequest request, 
        HttpServletResponse response, 
        Object handler, 
        Exception ex) {
        
        try {
            if (ex instanceof NoSuchRequestHandlingMethodException) {
                return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response, handler);
            } else 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 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 handleMethodArgumentNotValid((MethodArgumentNotValidException) ex, request, response, handler);
            } else if (ex instanceof MissingServletRequestPartException) {
                return handleMissingServletRequestPart((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);
            }
        } catch (Exception handlerException) {
            logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
        }
        return null;
    }
}

DefaultHandlerExceptionResolver通过判断异常类型,调用相应的处理方法。例如,如果抛出的是NoSuchRequestHandlingMethodException异常,则调用handleNoSuchRequestHandlingMethod方法进行处理。

3.2 ExceptionHandlerExceptionResolver

ExceptionHandlerExceptionResolver是另一个重要的异常处理器,用于处理通过@ExceptionHandler注解定义的异常处理方法。其源码如下:

public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver {
    @Override
    protected ModelAndView doResolveException(
        HttpServletRequest request, 
        HttpServletResponse response, 
        Object handler, 
        Exception ex) {
        
        ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handler, ex);
        if (exceptionHandlerMethod == null) {
            return null;
        }
        
        exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        try {
            exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, ex);
        } catch (Exception invocationEx) {
            logger.error("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
            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());
            return mav;
        }
    }
}

ExceptionHandlerExceptionResolver通过getExceptionHandlerMethod方法获取处理当前异常的@ExceptionHandler方法,然后调用invokeAndHandle方法执行该方法,并返回相应的ModelAndView

4. 自定义异常处理

除了使用SpringMVC提供的默认异常处理器外,开发者还可以通过实现HandlerExceptionResolver接口或使用@ControllerAdvice注解来自定义异常处理。

4.1 实现HandlerExceptionResolver接口

开发者可以通过实现HandlerExceptionResolver接口来自定义异常处理器。例如:

public class CustomHandlerExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(
        HttpServletRequest request, 
        HttpServletResponse response, 
        Object handler, 
        Exception ex) {
        
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("exception", ex);
        modelAndView.setViewName("error");
        return modelAndView;
    }
}

然后,在Spring配置文件中注册该异常处理器:

<bean class="com.example.CustomHandlerExceptionResolver"/>

4.2 使用@ControllerAdvice注解

@ControllerAdvice注解是SpringMVC提供的一种全局异常处理机制。通过该注解,开发者可以定义一个全局的异常处理类,处理所有控制器抛出的异常。例如:

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public ModelAndView handleException(Exception ex) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("exception", ex);
        modelAndView.setViewName("error");
        return modelAndView;
    }
}

@ControllerAdvice注解的类会自动被SpringMVC扫描并注册为全局异常处理器。

5. 异常处理流程

SpringMVC的异常处理流程如下:

  1. 控制器方法执行过程中抛出异常。
  2. SpringMVC框架捕获异常,并调用所有注册的HandlerExceptionResolverresolveException方法。
  3. 如果某个HandlerExceptionResolver成功处理了异常,则返回相应的ModelAndView
  4. 如果没有HandlerExceptionResolver能够处理异常,则继续抛出异常,最终由Servlet容器处理。

6. 源码分析

6.1 DispatcherServlet中的异常处理

DispatcherServlet是SpringMVC的核心控制器,负责处理所有的HTTP请求。在DispatcherServletprocessHandlerException方法中,会调用所有注册的HandlerExceptionResolver来处理异常:

protected ModelAndView processHandlerException(
    HttpServletRequest request, 
    HttpServletResponse response, 
    Object handler, 
    Exception ex) throws Exception {
    
    ModelAndView exMv = null;
    for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
        exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
        if (exMv != null) {
            break;
        }
    }
    if (exMv != null) {
        if (exMv.isEmpty()) {
            return null;
        }
        return exMv;
    }
    throw ex;
}

processHandlerException方法会遍历所有注册的HandlerExceptionResolver,直到某个HandlerExceptionResolver成功处理异常并返回ModelAndView

6.2 ExceptionHandlerExceptionResolver中的异常处理

ExceptionHandlerExceptionResolver是SpringMVC中用于处理@ExceptionHandler注解的异常处理器。其核心方法是doResolveException,源码如下:

@Override
protected ModelAndView doResolveException(
    HttpServletRequest request, 
    HttpServletResponse response, 
    Object handler, 
    Exception ex) {
    
    ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handler, ex);
    if (exceptionHandlerMethod == null) {
        return null;
    }
    
    exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    try {
        exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, ex);
    } catch (Exception invocationEx) {
        logger.error("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
        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());
        return mav;
    }
}

doResolveException方法首先通过getExceptionHandlerMethod方法获取处理当前异常的@ExceptionHandler方法,然后调用invokeAndHandle方法执行该方法,并返回相应的ModelAndView

7. 总结

SpringMVC提供了强大的异常处理机制,通过HandlerExceptionResolver接口和@ExceptionHandler注解,开发者可以灵活地处理控制器方法执行过程中抛出的异常。本文通过分析SpringMVC异常处理的源码,详细介绍了其工作原理和实现细节,希望能帮助读者更好地理解和使用SpringMVC的异常处理机制。

推荐阅读:
  1. springmvc 结合ajax如何实现批量增加
  2. 怎么在SpringMVC中使用Jquery实现Ajax功能

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

springmvc

上一篇:如何将json字符串转为php对象

下一篇:elasticsearch的match_phrase_prefix查询怎么使用

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》