SpringMVC原理分析(十二)--异常处理流程

1.自定义全局异常的使用

项目中经常需要自定义全局异常处理器来处理可能发生的异常,实例如下:

1)首先添加异常处理类,添加上@ControllerAdvice,一般也会在类上添加@ResponseBody向调用方返回错误信息,然后添加处理方法,在方法上添加@ExceptionHandler注解指定要处理的异常类型

@ControllerAdvice
@ResponseBody
public class MyExceptionHandler {
    @ExceptionHandler(value = ArithmeticException.class)
    public Result<String>  handleArithmeticException(ArithmeticException e) {
        System.out.println("处理ArithmeticException");
        return Result.fail("计算异常");
    }
}

2)添加控制器类

@RestController
public class Controller01 {
    @GetMapping(value = "/test")
    public Result<String> test() {
        System.out.println(1 / 0);
        return Result.ok("1");
    }
}

3)添加实体类

@Getter
@Setter
public class Result<T> {
    private Integer code;
    private String msg;
    private T data;

    public static <T> Result<T> ok(T data) {
        Result<T> result = new Result<>();
        result.setCode(0);
        result.setData(data);
        return result;
    }

    public static <T> Result<T> fail(String msg) {
        Result<T> result = new Result<>();
        result.setCode(-1);
        result.setMsg(msg);
        return result;
    }
}

4)添加测试类,并启动

@SpringBootApplication
public class Test {
    public static void main(String[] args) {
        SpringApplication.run(Test.class, args);
    }
}

浏览器调用http://127.0.0.1:8080/test,响应{"code": -1,"msg": "计算异常","data": null}

如果在控制器方法中也添加一个@ExceptionHandler修饰的方法,那么会优先执行控制器中的异常处理方法,响应{"code": -1,"msg": "控制器中计算异常","data": null}

    @ExceptionHandler(value = ArithmeticException.class)
    public Result<String>  handleArithmeticException(ArithmeticException e) {
        System.out.println("控制器中处理ArithmeticException");
        return Result.fail("控制器中计算异常");
    }

2.解析自定义异常处理器

DispatcherServlet初始化时会调用initHandlerExceptionResolvers初始化异常处理解析器,如果没有自定义异常处理解析器,默认会初始化DispatcherServlet.properties配置的类,如下:

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
    org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

ExceptionHandlerExceptionResolver的构造函数中会添加默认的消息转换器,因为处理响应时需要依赖消息转换器进行对象转换

public ExceptionHandlerExceptionResolver() {
    this.messageConverters = new ArrayList<>();
    // ByteArrayHttpMessageConverter支持application/octet-stream、*/*
    this.messageConverters.add(new ByteArrayHttpMessageConverter());
    // StringHttpMessageConverter支持text/plain、*/*
    this.messageConverters.add(new StringHttpMessageConverter());
    try {
        // SourceHttpMessageConverter支持text/xml、application/xml、application/*-xml.
        this.messageConverters.add(new SourceHttpMessageConverter<>());
    }
    catch (Error err) {

    }
    // AllEncompassingFormHttpMessageConverter根据加载类的情况添加消息转换器
    this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}

同时ExceptionHandlerExceptionResolver也实现了InitializingBean,初始化时会调用afterPropertiesSet()

public void afterPropertiesSet() {
    // 初始化ControllerAdviceBean中的ExceptionHandler
    initExceptionHandlerAdviceCache();

    // 初始化参数解析器
    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    // 初始化返回值解析器
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

private void initExceptionHandlerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }

    // 从容器中获取@ControllerAdvice注解修饰的类
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }
        ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
        if (resolver.hasExceptionMappings()) {
            // 如果有异常处理方法映射,添加到exceptionHandlerAdviceCache集合中
            this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
        }
        if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            this.responseBodyAdvice.add(adviceBean);
        }
    }

    // 省略打印日志代码...
}

创建ExceptionHandlerMethodResolver时会解析ControllerAdviceBean中被@ExceptionHandler注解修饰的方法

public class ExceptionHandlerMethodResolver {
    // 筛选有@ExceptionHandler注解的方法
    public static final MethodFilter EXCEPTION_HANDLER_METHODS = method ->
        AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);

    private final Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16);

    public ExceptionHandlerMethodResolver(Class<?> handlerType) {
        // 遍历类中被@ExceptionHandler注解的方法
        for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
            // 遍历@ExceptionHandler注解中定义的每个异常类型
            for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
                // 添加异常类型与异常处理方法的映射关系
                addExceptionMapping(exceptionType, method);
            }
        }
    }
}

private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
    List<Class<? extends Throwable>> result = new ArrayList<>();
    // 将@ExceptionHandler注解中value的值添加到result集合中
    detectAnnotationExceptionMappings(method, result);
    if (result.isEmpty()) {
        for (Class<?> paramType : method.getParameterTypes()) {
            if (Throwable.class.isAssignableFrom(paramType)) {
                result.add((Class<? extends Throwable>) paramType);
            }
        }
    }
    if (result.isEmpty()) {
        throw new IllegalStateException("No exception types mapped to " + method);
    }
    return result;
}

private void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
    ExceptionHandler ann = AnnotatedElementUtils.findMergedAnnotation(method, ExceptionHandler.class);
    Assert.state(ann != null, "No ExceptionHandler annotation");
    result.addAll(Arrays.asList(ann.value()));
}

private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
    Method oldMethod = this.mappedMethods.put(exceptionType, method);
    if (oldMethod != null && !oldMethod.equals(method)) {
        throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
                                        exceptionType + "]: {" + oldMethod + ", " + method + "}");
    }
}

这样,DispatcherServlet初始化时就解析了上述示例中自定义的异常处理器

3.异常处理流程

当服务器接收到请求时,会调用DispatcherServlet#doDispatch执行方法调用,如果调用过程中出现异常,则会进入到异常处理逻辑,调用DispatcherServlet#processHandlerException

protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
                                               @Nullable Object handler, Exception ex) throws Exception {
    request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

    ModelAndView exMv = null;
    if (this.handlerExceptionResolvers != null) {
        // 遍历HandlerExceptionResolver,调用resolveException
        for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
            exMv = resolver.resolveException(request, response, handler, ex);
            if (exMv != null) {
                break;
            }
        }
    }

    // 省略其他代码...
}

AbstractHandlerExceptionResolver#resolveException源码如下:

public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
    if (shouldApplyTo(request, handler)) {
        prepareResponse(ex, response);
        // 调用doResolveException
        ModelAndView result = doResolveException(request, response, handler, ex);
        if (result != null) {
            if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
                logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
            }
            logException(ex, request);
        }
        return result;
    }
    else {
        return null;
    }
}

protected final ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
    return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
}

然后会调用到ExceptionHandlerExceptionResolver#doResolveHandlerMethodException

protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
                                                       HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
    // 获取异常处理方法
    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 webRequest = new ServletWebRequest(request, response);
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();

    try {
        if (logger.isDebugEnabled()) {
            logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
        }
        Throwable cause = exception.getCause();
        if (cause != null) {
            // 执行异常处理方法
            exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
        }
        else {
            // 执行异常处理方法
            exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
        }
    }
    catch (Throwable invocationEx) {
        if (invocationEx != exception && invocationEx != exception.getCause() && logger.isWarnEnabled()) {
            logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
        }
        return null;
    }

    // 如果请求被设置为已处理,直接返回空的ModelAndView
    if (mavContainer.isRequestHandled()) {
        return new ModelAndView();
    }
    // 否则从ModelAndView容器中获取view和model,并构造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;
    }
}

获取异常处理方法逻辑如下:

1)先从当前控制器中查找有没有异常对应的@ExceptionHandler注解修饰的方法

2)如果没有查找到,则从全局异常处理器中查找

protected ServletInvocableHandlerMethod getExceptionHandlerMethod(@Nullable HandlerMethod handlerMethod, Exception exception) {
    Class<?> handlerType = null;
    
    if (handlerMethod != null) {
        // 从exceptionHandlerCache中获取ExceptionHandlerMethodResolver
        handlerType = handlerMethod.getBeanType();
        ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
        if (resolver == null) {
            // 根据handlerType创建ExceptionHandlerMethodResolver
            resolver = new ExceptionHandlerMethodResolver(handlerType);
            this.exceptionHandlerCache.put(handlerType, resolver);
        }
        // 如果控制器类中有@ExceptionHandler修饰的方法,直接返回此方法对应的ServletInvocableHandlerMethod
        Method method = resolver.resolveMethod(exception);
        if (method != null) {
            return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
        }
    
        if (Proxy.isProxyClass(handlerType)) {
            handlerType = AopUtils.getTargetClass(handlerMethod.getBean());
        }
    }
    
    // 如果当前控制器中没有定义对应的异常处理方法,则从全局异常处理器中查找
    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);
            }
        }
    }
    
    return null;
}

执行异常处理方法和执行控制器Handler方法逻辑一样

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                            Object... providedArgs) throws Exception {
    // 反射执行异常处理方法
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    setResponseStatus(webRequest);

    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            disableContentCachingIfNecessary(webRequest);
            mavContainer.setRequestHandled(true);
            return;
        }
    }
    else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }

    mavContainer.setRequestHandled(false);
    Assert.state(this.returnValueHandlers != null, "No return value handlers");
    try {
        // 通过返回值处理器处理方法执行结果
        this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    catch (Exception ex) {
        if (logger.isTraceEnabled()) {
            logger.trace(formatErrorForReturnValue(returnValue), ex);
        }
        throw ex;
    }
}

再通过invokeForRequest解析参数后,调用控制器方法执行请求

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                               Object... providedArgs) throws Exception {
    // 获取方法执行参数
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    // 真正反射执行目标方法
    return doInvoke(args);
}

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                           Object... providedArgs) throws Exception {
    // 获取参数列表
    MethodParameter[] parameters = getMethodParameters();
    if (ObjectUtils.isEmpty(parameters)) {
        return EMPTY_ARGS;
    }
    
    Object[] args = new Object[parameters.length];
    // 逐个处理每个参数
    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];
        // 初始化参数名称解析器
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        // 从给定参数中寻找,如果能匹配到就不通过参数解析器进行解析了
        // providedArgs一般为空,invocableMethod.invokeAndHandle调用时可以传入一些参数
        args[i] = findProvidedArgument(parameter, providedArgs);
        if (args[i] != null) {
            continue;
        }
        // 判断参数解析器能够支持此参数,这里的参数解析器是HandlerMethodArgumentResolverComposite组合器,会依次匹配每个参数解析器
        if (!this.resolvers.supportsParameter(parameter)) {
            throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
        }
        try {
            // 通过参数解析器解析参数
            args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
        }
        catch (Exception ex) {
            if (logger.isDebugEnabled()) {
                String exMsg = ex.getMessage();
                if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                    logger.debug(formatArgumentError(parameter, exMsg));
                }
            }
            throw ex;
        }
    }
    return args;
}

最终调用doInvoke()通过方法反射调用目标控制器方法

protected Object doInvoke(Object... args) throws Exception {
    // 如果不是public修饰的方法,则setAccessible(true)
    ReflectionUtils.makeAccessible(getBridgedMethod());
    try {
        // 通过方法反射调用,getBridgedMethod如果不是桥接的方法,返回的就是方法本身
        return getBridgedMethod().invoke(getBean(), args);
    }
    // 省略异常处理代码...
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lm_ylj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值