详解Spring boot/Spring 统一错误处理方案的使用

作者:明人不说暗话___我喜欢你 时间:2023-11-24 12:56:07 

当我们开发spring web应用程序时,对于如 IOException , ClassNotFoundException 之类的检查异常,往往编译器会提示程序员采用 try-catch 进行显式捕获,而对于像 ClassCastException , NullPointerException 这类非检查异常,编译器是不会提示你了,这往往也是能体现程序员代码编写能力的一个方面。

在spring web特别是spring-boot应用中,当一个请求调用成功时,一般情况下会返回 json 格式的对象,就像下面图所示:

详解Spring boot/Spring 统一错误处理方案的使用 

但如果请求抛出了一个 RuntimeException 呢?如果我们不做处理,再次调用时将出现下面的页面:

详解Spring boot/Spring 统一错误处理方案的使用

也就是说当调用出现错误时,spring-boot默认会将请求映射到 /error 路径中去,如果没有相应的路径请求处理器,那么就会返回上面的 Whitelabel 错误页面。

1、自定义错误处理页面

当然对运行时异常不做处理是不可能的啦!通常的做法是自定义统一错误页面,然后返回。按照上面的思路,我们实现一个请求路径为 /error 的控制器,控制器返回一个资源路径地址,定义请求映射路径为 /error 的控制器并实现 ErrorController 接口,代码如下:

MyErrorPageController


package com.example.demo.controller.handler.errorpage;

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
*
* The class MyErrorPageController.
*
* Description:自定义错误页面
*
* @author: huangjiawei
* @since: 2018年6月13日
* @version: $Revision$ $Date$ $LastChangedBy$
*
*/
@Controller
public class MyErrorPageController implements ErrorController {

@RequestMapping("/error")
 public String handleError() {
 return "error.html"; // 该资源位于resources/static目录下
 }

@Override
 public String getErrorPath() {
 return null;
 }
}

然后在 reosurces/static 目录下建立 error.html 文件:


<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>这是个错误页面!存放在resources/static目录下,spring-boot发生错误时默认调用</h1>
</body>
</html>

再次请求 http://localhost:7000/demo/getUserInfoWithNoHandler.json ,如下:

详解Spring boot/Spring 统一错误处理方案的使用 2、使用 @ControllerAdvice@ResponseBody@ExceptionHandler 统一处理异常

在spring中可以使用上面3个注解进行统一异常处理,默认情况下我们可以针对系统中出现的某种类型的异常定义一个统一的处理器handler,比如说系统抛出了一个 NullPointerException ,那么我们可以定义一个专门针对 NullPointerException 的处理器,代码如下:

getUserInfoWithNullPointerException 接口


/**
* 测试空指针错误的处理
* @return
* @throws NullPointerException
*/
@RequestMapping(value = "getUserInfoWithNullPointerException.json", method = RequestMethod.GET)
public Student getUserInfoWithNullPointerException() throws NullPointerException {
throw new NullPointerException();
}

NullPointerExceptionHandler.java


package com.example.demo.controller.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.demo.pojo.ErrorReturn;

/**
*
* The class NullPointerExceptionHandler.
*
* Description:处理空指针
*
* @author: huangjiawei
* @since: 2018年6月13日
* @version: $Revision$ $Date$ $LastChangedBy$
*
*/
@ControllerAdvice
public class NullPointerExceptionHandler {
 @ExceptionHandler(NullPointerException.class)
 @ResponseBody
 public ErrorReturn dealNullPointerException() {
   e.printStackTrace();
 ErrorReturn error = new ErrorReturn();
 error.setReturnCode("-1");
 error.setDesc("出现空指针异常啦!");
 return error;
 }
}

浏览器执行: http://localhost:7000/demo/getUserInfoWithNullPointerException.json

详解Spring boot/Spring 统一错误处理方案的使用

同样的道理,如果我们还需要为其他的运行时异常提供统一的处理器,那么也可以像上面一样为每一个异常类型定义一个处理器,比如我们又想为 ArithmeticException 定义处理器,那么我们只需要建立一个类或者方法,然后在方法上的 @ExceptionHanler 注解内加上 ArithmeticException.class 指定异常类型即可。

不过你有没有发现,这样为每种异常类型定义一个异常处理类或者方法,因为运行时异常类型特别多,不可能为每种类型都指定一个处理器类或方法,针对这种情况,spring也是可以解决的。如果我们没有为某种特定类型异常,如 ArithmeticException 定义处理器,那么我们可以定义一个 Exception 或者 Throwable 处理器统一处理。

这样做的好处是,减少了处理器类的数量,同时将异常处理转移到父类上面去,这也是继承的一大优势吧!但是,当你既定义了特定类型的异常,同时又定义了 Exception 异常的处理器,那么要小心了,这里不一定有优先级的关系,也就是说不一定会出现只执行父异常处理器的情况,可能是只执行A处理器,而不执行B处理器或者只执行B处理器,不执行A处理器。如 NullPointerExceptionHandler 异常会向 Exception 异常传递(但 ArithmeticException 不会向 Exception 传递)

现在假设我们既定义上面的 NullPointerExceptionHandler ,又定义了下面的 ExceptionThrowableHandler ,那么当发生 NullPointerException 时,就会默认执行 ExceptionThrowableHandler 的方法。

ExceptionThrowableHandler.java


package com.example.demo.controller.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.demo.pojo.ErrorReturn;

/**
*
* The class ExceptionThrowableHandler.
*
* Description:有些异常会向高级别异常传递(但ArithmeticException不会向Exception传送)
*
* @author: huangjiawei
* @since: 2018年6月13日
* @version: $Revision$ $Date$ $LastChangedBy$
*
*/
@ControllerAdvice
public class ExceptionThrowableHandler {

@ExceptionHandler(Throwable.class)
 @ResponseBody
 public ErrorReturn dealThrowable() {
 ErrorReturn error = new ErrorReturn();
 error.setDesc("处理Throwable!");
 error.setReturnCode("-1");
 return error;
 }

@ExceptionHandler(Exception.class)
 @ResponseBody
 public ErrorReturn dealCommonException() {
 ErrorReturn error = new ErrorReturn();
 error.setReturnCode("-1");
 error.setDesc("公共异常处理!");
 return error;
 }
}

浏览器执行 : http://localhost:7000/demo/getUserInfoWithNullPointerException.json

详解Spring boot/Spring 统一错误处理方案的使用

可以发现只执行 Exception 的处理器,没有执行空指针的处理器,也就是异常处理往上传送了。下面再来看看抛出 ArithmeticException 的情况:

getUserInfoWithArithmeticException.json


/**
* 测试空指针错误的处理
* @return
* @throws NullPointerException
*/
@RequestMapping(value = "getUserInfoWithArithmeticException.json", method = RequestMethod.GET)
public Student getUserInfoWithArithmeticException() throws ArithmeticException {
throw new ArithmeticException();
}

ArithmeticExceptionHandler.java


package com.example.demo.controller.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.demo.pojo.ErrorReturn;

@ControllerAdvice
public class ArithmeticExceptionHandler {
 /**
  * 处理ArithmeticException异常
  * @return
  */
 @ResponseBody
 @ExceptionHandler(ArithmeticException.class)
 public ErrorReturn dealArithmeticException() {
 ErrorReturn errorObject = new ErrorReturn();
 errorObject.setReturnCode("-1");
 errorObject.setDesc("算数处理出现异常!");
 return errorObject;
 }
}

浏览器执行 : http://localhost:7000/demo/getUserInfoWithArithmeticException.json

详解Spring boot/Spring 统一错误处理方案的使用

结果发现异常处理并没有往上层的 ExceptionHandler 传送。

总结:对于既定义特定类型的处理器,又定义 Exception 等父类型的处理器时要特别小心,并不是所有的异常都会往上级处理,如果我们想只减少处理器类的数量,不想为每种特定类型的处理器添加类或者方法,那么小编建议使用 instanceof 关键字对异常类型进行判断即可。

如下面的代码,我们只建立一个公共的异常处理器,处理 Exception 异常,同时使用 instanceof 进行判断。


@ExceptionHandler(Exception.class)
@ResponseBody
public ErrorReturn dealCommonException(Exception e) {
 ErrorReturn error = new ErrorReturn();
 // 此处可以采用 instanceof 判断异常类型
 if (e instanceof ArithmeticException) {
 error.setReturnCode("-1");
 error.setDesc("算数异常处理!");
 return error;
 }
 System.err.println("exception");
 error.setReturnCode("-1");
 error.setDesc("公共异常处理!");
 return error;
}

浏览器执行抛出 ArithmeticException 的接口,如下:

详解Spring boot/Spring 统一错误处理方案的使用

本文代码地址: https://github.com/SmallerCoder/spring_exceptionHandler

来源:https://juejin.im/post/5b2101716fb9a01e80785be2

标签:Spring,统一错误,Springboot
0
投稿

猜你喜欢

  • 解析Java的JNI编程中的对象引用与内存泄漏问题

    2023-03-19 20:59:28
  • Android TabLayout(选项卡布局)简单用法实例分析

    2023-12-17 09:52:28
  • mybatis 查询返回Map<String,Object>类型

    2023-11-14 07:06:09
  • JavaFX 监听窗口关闭事件实例详解

    2023-07-20 21:53:06
  • android studio 项目 :UI设计高精度实现简单计算器

    2022-07-14 02:09:29
  • spring boot实战教程之shiro session过期时间详解

    2023-01-05 01:37:47
  • 全面解析Java支持的数据类型及Java的常量和变量类型

    2022-03-25 16:54:10
  • struts2中使用注解配置Action方法详解

    2023-08-30 00:01:25
  • Java基于Socket实现HTTP下载客户端

    2022-07-06 01:02:30
  • Android AIDL和远程Service调用示例代码

    2022-07-14 03:06:15
  • Java带返回值的方法的定义和调用详解

    2022-10-05 21:01:09
  • Android中AutoCompleteTextView与TextWatcher结合小实例

    2023-11-07 06:27:22
  • Android学习笔记之ActionBar Item用法分析

    2022-03-12 00:18:59
  • Flutter实现底部导航栏创建详解

    2023-09-29 10:01:04
  • unity shader实现玻璃折射效果

    2022-04-16 08:22:36
  • Spring实现内置监听器

    2021-07-06 01:29:06
  • Android中CountDownTimer 实现倒计时功能

    2022-01-03 00:00:24
  • 浅谈@Aspect@Order各个通知的执行顺序

    2021-10-24 19:00:38
  • Java 回调函数详解及使用

    2023-11-03 02:32:35
  • Android提高之多级树形菜单的实现方法

    2021-08-27 08:26:48
  • asp之家 软件编程 m.aspxhome.com