011-Spring MVC全局异常处理

使用 @ExceptionHandler 处理异常

@ExceptionHandler 是作用在方法上的注解,在对应的Controller中定义 @ExceptionHandler 注解的方法即可处理当前 Controller 对应的异常。

package com.yyoo.springmvc.controller;

import com.yyoo.springmvc.bean.MyResponse;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/demo5")
public class Demo5Controller {

    @ExceptionHandler(Exception.class)
    public MyResponse exceptionHandler(Exception ex){
        return MyResponse.success(ex.getMessage());
    }

    @RequestMapping("testException")
    public MyResponse testException(){
        if(true){
            throw new RuntimeException("发生异常");
        }

        return MyResponse.success();
    }

}

@ExceptionHandler(Exception.class) 表示处理所以Exception或其子类。如果我们需要更细的异常控制,我们可以定义多个由@ExceptionHandler 注解的方法,然后对应的Exception.class不一样。

如果我们需要同一个方法处理多种类型的异常:@ExceptionHandler({Exception1.class,Exception2.class})

@ExceptionHandler 注解支持的方法参数

方法参数描述
Exception用于访问引发的异常(我们都示例中就是Exception)。
HandlerMethod用于访问引发异常的控制器方法。
WebRequest, NativeWebRequest对请求参数以及请求和会话属性的通用访问,无需直接使用 Servlet API。
javax.servlet.ServletRequest, javax.servlet.ServletResponse选择任何特定的请求或响应类型(例如,ServletRequestor HttpServletRequest或 Spring 的MultipartRequestor MultipartHttpServletRequest)。
javax.servlet.http.HttpSession强制会话的存在。因此,这样的论证永远不会null。请注意,会话访问不是线程安全的。多个请求是可以同时访问一个会话。
java.security.Principal当前经过身份验证的用户 -Principal如果已知,可能是特定的实现类。
HttpMethod请求的 HTTP 方法。
java.util.Locale当前请求区域设置,由最具体的LocaleResolver可用区域确定- 实际上,配置的LocaleResolver或LocaleContextResolver.
java.util.TimeZone, java.time.ZoneId与当前请求关联的时区,由 确定LocaleContextResolver。
java.io.OutputStream, java.io.Writer用于访问由 Servlet API 公开的原始响应正文。
java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap用于访问模型以获得错误响应。总是空的。
RedirectAttributes指定在重定向的情况下使用的属性 - (即附加到查询字符串)和要临时存储的 flash 属性,直到重定向后的请求。
@SessionAttribute用于访问任何会话属性,与作为类级别@SessionAttributes声明的结果存储在会话中的模型属性相反。
@RequestAttribute用于访问请求属性。

@ExceptionHandler 注解支持的方法返回值

返回值描述
@ResponseBody返回值通过HttpMessageConverter实例转换并写入响应。
HttpEntity, ResponseEntity返回值指定完整的响应(包括 HTTP 标头和正文)通过HttpMessageConverter实例转换并写入响应。
String要与ViewResolver实现一起解析并与隐式模型一起使用的视图名称
ViewView实例以使用用于与所述隐式模型一起渲染
java.util.Map, org.springframework.ui.Model要添加到隐式模型的属性
@ModelAttribute要添加到模型的属性
void用的方法void返回类型(或null返回值)被认为已经完全处理的响应。

@ControllerAdvice 或 @RestControllerAdvice 统一处理异常

@ControllerAdvice 和 @RestControllerAdvice 与 @Controller 或 @RestController 意思是一样的,这里不在做过多讲解了。
@ControllerAdvice 和 @RestControllerAdvice 是作用在类上的注解,用于SpringMVC的全局异常处理。通常情况下我们会使用@ControllerAdvice 或 @RestControllerAdvice 来定义我们都全局处理异常类。

package com.yyoo.springmvc.config;

import com.yyoo.springmvc.bean.MyResponse;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public MyResponse exceptionHandler(Exception e){
        // 注:这里的e.getMessage可能是大量的异常信息,实际情况下请勿直接使用
        // 为了方便异常排查,此处一般我们还得打印日志或者进行日志的处理
        return MyResponse.error(e.getMessage());
    }

}

@RestControllerAdvice 注解的Bean中 @ExceptionHandler 的使用方法跟上面描述一样。意味着我们都全局异常处理也可以有多个处理方法来处理不同类型的异常。我们示例中是直接处理的顶层异常类,此处理方式还是有些问题的,特别是我们直接返回的e.getMessage。那么我们直接返回汉字"出现异常"可以吗?当然可以,但是这意味着我们的所有的异常最终的提示信息都一样了。

建议的处理方式为:

  1. 自定义一个异常类(如:MyException - 建议为RuntimeException,这样会少去很多try … catch)
  2. 在Controller 或 Service 中所有我们可以判断的业务类异常都通过该异常抛出,并对应指定异常信息,方便前端提示。
  3. 对Exception异常进行处理,现在进入Exception异常处理的异常通常都是些代码错误或者bug导致,直接提示“服务端异常”即可。

@ControllerAdvice 和 @ExceptionHandler

如果我们同时配置了@ControllerAdvice 和 @ExceptionHandler,则Spring MVC会优先匹配 @ExceptionHandler 的方法。

应用中有多个@ControllerAdvice或@RestControllerAdvice的执行顺序

应用中有多个@ControllerAdvice或@RestControllerAdvice的时候,其顺序默认是按照bean的名称来执行的,如果需要自己指定,可以使用@Order注解来指定。建议指定该顺序,否则会有些问题,比如:

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Slf4j
@RestControllerAdvice
@Order(10)
public class TestExceptionHandler {

    /**
     * 自定义异常处理
     * @param e
     * @return
     */
    @ExceptionHandler(TestException.class)
    public MyResponse<String> myExceptionHandler(TestException e){
        log.error("",e);
        return MyResponse.error("打印出 TestException 异常");
    }

}

TestException 继承自 RuntimeException

@Slf4j
@RestControllerAdvice
@Order(2)
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public MyResponse<String> exceptionHandler(Exception e){
        log.error("",e);
        return MyResponse.error("系统异常");
    }
}

GlobalExceptionHandler 的顺序为2,TestExceptionHandler 顺序为 10,说明在执行时,GlobalExceptionHandler 在前,这时,当应用出现 TestExceptionHandler 异常时,将会通过 GlobalExceptionHandler 来处理,此处正确的顺序应该是 TestExceptionHandler 在前,GlobalExceptionHandler 在后。如果不使用 @Order 注解,就可能导致该问题的出现

上一篇:Spring MVC 拦截器及其源码解析
下一篇:Spring MVC 异常处理源码解析

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值