Spring Boot 统一处理全局异常

举报
小小张自由--张有博 发表于 2021/12/02 22:47:49 2021/12/02
【摘要】 目录 注解的介绍 @ControllerAdvice @ExceptionHandler拦截异常并统一处理 代码实现 自定义异常 统一异常处理 前端返回值类 测试用例 如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏哦。 注解的介绍 @ControllerAdvice @C...

目录

注解的介绍

@ControllerAdvice

@ExceptionHandler拦截异常并统一处理

代码实现

自定义异常

统一异常处理

前端返回值类

测试用例

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏哦。


注解的介绍

@ControllerAdvice

@ControllerAdvice注解是Spring3.2中新增的注解,学名是Controller增强器,作用是给Controller控制器添加统一的操作或处理。

这里ControllerAdvice也可以这么理解,其抽象级别应该是用于对Controller进行切面环绕的,而具体的业务织入方式则是通过结合其他的注解来实现的。@ControllerAdvice是在类上声明的注解,其用法主要有三点:

1.结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。

2.结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的。

3.结合方法型注解@ModelAttribute,表示其注解的方法将会在目标Controller方法执行之前执行。

从上面的讲解可以看出,@ControllerAdvice的用法基本是将其声明在某个bean上,然后在该bean的方法上使用其他的注解来指定不同的织入逻辑。不过这里@ControllerAdvice并不是使用AOP的方式来织入业务逻辑的,而是Spring内置对其各个逻辑的织入方式进行了内置支持。

针对声明@ExceptionHandler 、 @InitBinder或@ModelAttribute方法的类的@Component @ExceptionHandler , @InitBinder在多个@Controller类之间共享。

使用@ControllerAdvice注解的类可以明确声明为 Spring bean 或通过类路径扫描自动检测。 所有此类 bean 都根据Ordered语义或@Order / @Priority声明进行Ordered , Ordered语义优先于@Order / @Priority声明。 然后在运行时按该顺序应用@ControllerAdvice bean。 但是请注意,实现PriorityOrdered @ControllerAdvice bean 的PriorityOrdered不高于实现Ordered @ControllerAdvice bean。 此外, Ordered不适用于范围内的@ControllerAdvice例如,如果这样的 bean 已被配置为请求范围或会话范围的 bean。 对于处理异常, @ExceptionHandler将在第一个具有匹配异常处理程序方法的通知中被选择。 对于模型的属性和数据绑定初始化, @ModelAttribute和@InitBinder方法将遵循@ControllerAdvice秩序。

注意:对于@ExceptionHandler方法,在特定建议 bean 的处理程序方法中,根异常匹配将优先于仅匹配当前异常的原因。 但是,与较低优先级建议 bean 上的任何匹配(无论是根还是原因级别)相比,更高优先级建议上的原因匹配仍然是首选。 因此,请在具有相应顺序的优先建议 bean 上声明您的主要根异常映射。

默认情况下, @ControllerAdvice ControllerAdvice 中的方法全局应用于所有控制器。 使用诸如annotations 、 basePackageClasses和basePackages (或其别名value )之类的选择器来定义目标控制器的更窄子集。 如果声明了多个选择器,则应用布尔OR逻辑,这意味着所选控制器应至少匹配一个选择器。 请注意,选择器检查是在运行时执行的,因此添加许多选择器可能会对性能产生负面影响并增加复杂性。

@ExceptionHandler拦截异常并统一处理

配合 @ExceptionHandler注解结合使用,当异常抛到controller层时,可以对异常进行统一的处理,规定返回的json格式或者跳转到指定的错误页面等.

@ExceptionHandler的作用主要在于声明一个或多个类型的异常,当符合条件的Controller抛出这些异常之后将会对这些异常进行捕获,然后按照其标注的方法的逻辑进行处理,从而改变返回的视图信息。

用于处理特定处理程序类和/或处理程序方法中的异常的注解。

使用此注解注释的处理程序方法允许具有非常灵活的签名。 它们可能具有以下类型的参数,按任意顺序排列:

异常参数:声明为一般异常或更具体的异常。 如果注解本身没有通过其value()缩小异常类型,这也可用作映射提示

代码实现

自定义异常


  
  1. /**
  2. * 自定义一个异常类,用于处理我们发生的业务异常
  3. *
  4. * @author Promsing(张有博)
  5. * @version 1.0.0
  6. * @since 2021/11/27 - 20:14
  7. */
  8. public class BizException extends RuntimeException {
  9. private static final long serialVersionUID = 1L;
  10. /**
  11. * 错误码
  12. */
  13. protected String errorCode;
  14. /**
  15. * 错误信息
  16. */
  17. protected String errorMsg;
  18. public BizException() {
  19. super();
  20. }
  21. public BizException(FrontResult errorInfoInterface) {
  22. super(errorInfoInterface.getCode());
  23. this.errorCode = errorInfoInterface.getMessage();
  24. this.errorMsg = errorInfoInterface.getMessage();
  25. }
  26. public BizException(FrontResult errorInfoInterface, Throwable cause) {
  27. super(errorInfoInterface.getCode(), cause);
  28. this.errorCode = errorInfoInterface.getCode();
  29. this.errorMsg = errorInfoInterface.getMessage();
  30. }
  31. public BizException(String errorMsg) {
  32. super(errorMsg);
  33. this.errorMsg = errorMsg;
  34. }
  35. public BizException(String errorCode, String errorMsg) {
  36. super(errorCode);
  37. this.errorCode = errorCode;
  38. this.errorMsg = errorMsg;
  39. }
  40. public BizException(String errorCode, String errorMsg, Throwable cause) {
  41. super(errorCode, cause);
  42. this.errorCode = errorCode;
  43. this.errorMsg = errorMsg;
  44. }
  45. public String getErrorCode() {
  46. return errorCode;
  47. }
  48. public void setErrorCode(String errorCode) {
  49. this.errorCode = errorCode;
  50. }
  51. public String getErrorMsg() {
  52. return errorMsg;
  53. }
  54. public void setErrorMsg(String errorMsg) {
  55. this.errorMsg = errorMsg;
  56. }
  57. public String getMessage() {
  58. return errorMsg;
  59. }
  60. @Override
  61. public Throwable fillInStackTrace() {
  62. return this;
  63. }
  64. }

统一异常处理


  
  1. import com.tfjy.arbackend.enumtool.ResultCodeEnum;
  2. import com.tfjy.arbackend.enumtool.ResutlMsgEnum;
  3. import com.tfjy.arbackend.util.FrontResult;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.web.bind.annotation.ControllerAdvice;
  7. import org.springframework.web.bind.annotation.ExceptionHandler;
  8. import org.springframework.web.bind.annotation.ResponseBody;
  9. import javax.servlet.http.HttpServletRequest;
  10. import java.io.IOException;
  11. import java.net.InetAddress;
  12. import java.net.UnknownHostException;
  13. import java.sql.SQLException;
  14. /**
  15. * 统一异常处理
  16. *
  17. * @author Promsing(张有博)
  18. * @version 1.0.0
  19. * @since 2021/11/27 - 20:14
  20. */
  21. @ControllerAdvice//使用该注解表示开启了全局异常的捕获
  22. public class GlobalExceptionHandler {
  23. private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
  24. /**
  25. * 处理自定义的业务异常
  26. * @param req
  27. * @param e
  28. * @return
  29. */
  30. @ExceptionHandler(value = BizException.class)
  31. @ResponseBody
  32. public FrontResult bizExceptionHandler(HttpServletRequest req, BizException e){
  33. logger.error("URL : " + req.getRequestURL().toString());
  34. logger.error("HTTP_METHOD : " + req.getMethod());
  35. logger.error("发生业务异常!原因是:{}",e.getErrorMsg());
  36. return FrontResult.getExceptionResult(e.getErrorCode(),e.getErrorMsg());
  37. }
  38. /**
  39. * 处理空指针的异常
  40. * @param req
  41. * @param e
  42. * @return
  43. */
  44. @ExceptionHandler(value =NullPointerException.class)
  45. @ResponseBody
  46. public FrontResult exceptionHandler(HttpServletRequest req, NullPointerException e) {
  47. logger.error("URL : " + req.getRequestURL().toString());
  48. logger.error("HTTP_METHOD : " + req.getMethod());
  49. logger.error("发生空指针异常!原因是:",e);
  50. return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
  51. }
  52. /**
  53. * 处理索引越界异常
  54. * @param req
  55. * @param e
  56. * @return
  57. */
  58. @ExceptionHandler(value =IndexOutOfBoundsException.class)
  59. @ResponseBody
  60. public FrontResult exceptionHandler(HttpServletRequest req, IndexOutOfBoundsException e){
  61. logger.error("URL : " + req.getRequestURL().toString());
  62. logger.error("HTTP_METHOD : " + req.getMethod());
  63. logger.error("索引越界异常!原因是:",e);
  64. return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
  65. }
  66. /**
  67. * 处理类未找到异常
  68. * @param req
  69. * @param e
  70. * @return
  71. */
  72. @ExceptionHandler(value =ClassNotFoundException.class)
  73. @ResponseBody
  74. public FrontResult exceptionHandler(HttpServletRequest req, ClassNotFoundException e) {
  75. logger.error("URL : " + req.getRequestURL().toString());
  76. logger.error("HTTP_METHOD : " + req.getMethod());
  77. logger.error("发生类未找到异常!原因是:",e);
  78. return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
  79. }
  80. /**
  81. * 处理SQL异常
  82. * @param req
  83. * @param e
  84. * @return
  85. */
  86. @ExceptionHandler(value = SQLException.class)
  87. @ResponseBody
  88. public FrontResult exceptionHandler(HttpServletRequest req, SQLException e) {
  89. logger.error("URL : " + req.getRequestURL().toString());
  90. logger.error("HTTP_METHOD : " + req.getMethod());
  91. logger.error("发生SQL异常!原因是:",e);
  92. return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
  93. }
  94. /**
  95. * 处理IO异常
  96. * @param req
  97. * @param e
  98. * @return
  99. */
  100. @ExceptionHandler(value = IOException.class)
  101. @ResponseBody
  102. public FrontResult exceptionHandler(HttpServletRequest req, IOException e) {
  103. logger.error("URL : " + req.getRequestURL().toString());
  104. logger.error("HTTP_METHOD : " + req.getMethod());
  105. logger.error("发生IO异常!原因是:",e);
  106. return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
  107. }
  108. /**
  109. * 处理其他异常
  110. * @param req
  111. * @param e
  112. * @return
  113. */
  114. @ExceptionHandler(value =Exception.class)
  115. @ResponseBody
  116. public FrontResult exceptionHandler(HttpServletRequest req, Exception e){
  117. logger.error("URL : " + req.getRequestURL().toString());
  118. logger.error("HTTP_METHOD : " + req.getMethod());
  119. logger.error("未知异常!原因是:",e);
  120. return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg());
  121. }
  122. }

前端返回值类


  
  1. import com.tfjy.arbackend.enumtool.ResultCodeEnum;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. @Data
  6. @AllArgsConstructor
  7. @NoArgsConstructor
  8. public class FrontResult {
  9. /**
  10. * 结果状态码
  11. */
  12. private String code;
  13. /**
  14. * 响应结果描述
  15. */
  16. private String message;
  17. /**
  18. * 返回数据
  19. */
  20. private Object data;
  21. /**
  22. * 静态方法,返回前端实体结果
  23. *
  24. * @param code 状态码
  25. * @param message 消息
  26. * @param data 数据
  27. * @return 前端实体结果
  28. */
  29. public static FrontResult build(String code, String message, Object data) {
  30. return new FrontResult(code, message, data);
  31. }
  32. /**
  33. * 返回成功的结果实体
  34. *
  35. * @param message 消息
  36. * @param data 数据
  37. * @return 实体
  38. */
  39. public static FrontResult getSuccessResult(String message, Object data) {
  40. FrontResult result = new FrontResult();
  41. result.code = ResultCodeEnum.SUCCESS.getCode();
  42. result.message = message;
  43. result.data = data;
  44. return result;
  45. }
  46. /**
  47. * 返回无需data的成功结果实体
  48. *
  49. * @param message 消息内容
  50. * @return 返回结果
  51. */
  52. public static FrontResult getSuccessResultOnlyMessage(String message) {
  53. FrontResult result = new FrontResult();
  54. result.code = ResultCodeEnum.SUCCESS.getCode();
  55. result.message = message;
  56. result.data = null;
  57. return result;
  58. }
  59. /**
  60. * 获取一个异常结果
  61. *
  62. * @param code 错误码
  63. * @param message 自定义异常信息
  64. * @return FrontResult
  65. */
  66. public static FrontResult getExceptionResult(String code, String message) {
  67. FrontResult result = new FrontResult();
  68. result.code = code.isEmpty() ? ResultCodeEnum.CODE_EXCEPTION.getCode() : code;
  69. result.message = message.isEmpty() ? ResultCodeEnum.CODE_EXCEPTION.getMsg() : message;
  70. return result;
  71. }
  72. }

  
  1. import lombok.AllArgsConstructor;
  2. @AllArgsConstructor
  3. public enum ResultCodeEnum {
  4. // 请求成功
  5. SUCCESS("0000"),
  6. // 请求失败
  7. FAIL("1111"),
  8. // EXCEL 导入失败
  9. EXCEL_FAIL("1000"),
  10. // userID 为空
  11. ID_NULL("1001"),
  12. // 前端传的实体为空
  13. MODEL_NULL("1002"),
  14. // 更新失败
  15. UPDATE_FAIL("1011"),
  16. // 参数为空
  17. PARAM_ERROR("400"),
  18. // 代码内部异常
  19. CODE_EXCEPTION("500", "代码内部异常");
  20. /**
  21. * 状态码
  22. */
  23. private String code;
  24. public String getCode() {
  25. return code;
  26. }
  27. ResultCodeEnum(String code) {
  28. this.code = code;
  29. }
  30. private String msg;
  31. public String getMsg() {
  32. return msg;
  33. }
  34. }
  35. public enum ResutlMsgEnum {
  36. //查询成功
  37. FIND_SUCCESS("查询成功!"),
  38. //查询失败
  39. FIND_FAIL("查询失败!"),
  40. //更新成功
  41. UPDATE_SUCCESS("更新成功"),
  42. //更新失败
  43. UPDATE_FAIL("更新成功"),
  44. SEND_SUCCESS("发送成功"),
  45. SEND_FAIL("发送失败");
  46. private String msg;
  47. ResutlMsgEnum(String msg) {
  48. this.msg = msg;
  49. }
  50. public String getMsg() {
  51. return msg;
  52. }
  53. }

测试用例


  
  1. /**
  2. * 测试用例
  3. *
  4. * @author Promsing(张有博)
  5. * @version 1.0.0
  6. * @since 2021/11/29 - 9:05
  7. */
  8. @Api(tags = {"测试controller"})
  9. @RequestMapping(value = "/testController")
  10. @RestController
  11. public class TestController {
  12. @ApiOperation(value = "测试null")
  13. @GetMapping(value = "getNull")
  14. public FrontResult getNull() {
  15. int length = 0;
  16. String name=null;
  17. length = name.length();
  18. return FrontResult.build(ResultCodeEnum.SUCCESS.getCode(),
  19. ResutlMsgEnum.EXECUTE_SUCCESS.getMsg(), length);
  20. }
  21. }

其他异常同理,也可以捕获。完美,没问题。全局统一异常处理设置成功。

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏哦。

文章来源: blog.csdn.net,作者:小小张自由—>张有博,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/promsing/article/details/121655576

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。