springboot 监听所有异常_SpringBoot优雅的全局异常处理

本文介绍了如何在SpringBoot项目中实现优雅的全局异常处理。通过使用@ControllerAdvice和@ExceptionHandler注解,自定义异常类和枚举,以及创建全局异常处理类,可以捕获并统一处理各种异常,包括自定义的BizException、空指针异常和其他未知异常。同时,文章提供了详细的代码示例和测试用例,展示了异常处理的完整流程。
摘要由CSDN通过智能技术生成

前言

本篇文章主要介绍的是SpringBoot项目进行全局异常的处理。

SpringBoot全局异常准备

说明:如果想直接获取工程那么可以直接跳到底部,通过链接下载工程代码。

开发准备

环境要求

JDK:1.8

SpringBoot:1.5.17.RELEASE

首先还是Maven的相关依赖:

UTF-8

1.8

1.8

1.8

org.springframework.boot

spring-boot-starter-parent

1.5.17.RELEASE

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-test

test

com.alibaba

fastjson

1.2.41

配置文件这块基本不需要更改,全局异常的处理只需在代码中实现即可。

代码编写

SpringBoot的项目已经对有一定的异常处理了,但是对于我们开发者而言可能就不太合适了,因此我们需要对这些异常进行统一的捕获并处理。SpringBoot中有一个ControllerAdvice的注解,使用该注解表示开启了全局异常的捕获,我们只需在自定义一个方法使用ExceptionHandler注解然后定义捕获异常的类型即可对这些捕获的异常进行统一的处理。

我们根据下面的这个示例来看该注解是如何使用吧。

示例代码:

@ControllerAdvice

public class MyExceptionHandler {

@ExceptionHandler(value =Exception.class)

public String exceptionHandler(Exception e){

System.out.println("未知异常!原因是:"+e);

return e.getMessage();

}

}

上述的示例中,我们对捕获的异常进行简单的二次处理,返回异常的信息,虽然这种能够让我们知道异常的原因,但是在很多的情况下来说,可能还是不够人性化,不符合我们的要求。

那么我们这里可以通过自定义的异常类以及枚举类来实现我们想要的那种数据吧。

自定义基础接口类

首先定义一个基础的接口类,自定义的错误描述枚举类需实现该接口。

代码如下:

public interface BaseErrorInfoInterface {

/** 错误码*/

String getResultCode();

/** 错误描述*/

String getResultMsg();

}

自定义枚举类

然后我们这里在自定义一个枚举类,并实现该接口。

代码如下:

public enum CommonEnum implements BaseErrorInfoInterface {

// 数据操作错误定义

SUCCESS("200", "成功!"),

BODY_NOT_MATCH("400","请求的数据格式不符!"),

SIGNATURE_NOT_MATCH("401","请求的数字签名不匹配!"),

NOT_FOUND("404", "未找到该资源!"),

INTERNAL_SERVER_ERROR("500", "服务器内部错误!"),

SERVER_BUSY("503","服务器正忙,请稍后再试!")

;

/** 错误码 */

private String resultCode;

/** 错误描述 */

private String resultMsg;

CommonEnum(String resultCode, String resultMsg) {

this.resultCode = resultCode;

this.resultMsg = resultMsg;

}

@Override

public String getResultCode() {

return resultCode;

}

@Override

public String getResultMsg() {

return resultMsg;

}

}

自定义异常类

然后我们在来自定义一个异常类,用于处理我们发生的业务异常。

代码如下:

public class BizException extends RuntimeException {

private static final long serialVersionUID = 1L;

/**

* 错误码

*/

protected String errorCode;

/**

* 错误信息

*/

protected String errorMsg;

public BizException() {

super();

}

public BizException(BaseErrorInfoInterface errorInfoInterface) {

super(errorInfoInterface.getResultCode());

this.errorCode = errorInfoInterface.getResultCode();

this.errorMsg = errorInfoInterface.getResultMsg();

}

public BizException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) {

super(errorInfoInterface.getResultCode(), cause);

this.errorCode = errorInfoInterface.getResultCode();

this.errorMsg = errorInfoInterface.getResultMsg();

}

public BizException(String errorMsg) {

super(errorMsg);

this.errorMsg = errorMsg;

}

public BizException(String errorCode, String errorMsg) {

super(errorCode);

this.errorCode = errorCode;

this.errorMsg = errorMsg;

}

public BizException(String errorCode, String errorMsg, Throwable cause) {

super(errorCode, cause);

this.errorCode = errorCode;

this.errorMsg = errorMsg;

}

public String getErrorCode() {

return errorCode;

}

public void setErrorCode(String errorCode) {

this.errorCode = errorCode;

}

public String getErrorMsg() {

return errorMsg;

}

public void setErrorMsg(String errorMsg) {

this.errorMsg = errorMsg;

}

public String getMessage() {

return errorMsg;

}

@Override

public Throwable fillInStackTrace() {

return this;

}

}

自定义数据格式

顺便这里我们定义一下数据的传输格式。

代码如下:

public class ResultBody {

/**

* 响应代码

*/

private String code;

/**

* 响应消息

*/

private String message;

/**

* 响应结果

*/

private Object result;

public ResultBody() {

}

public ResultBody(BaseErrorInfoInterface errorInfo) {

this.code = errorInfo.getResultCode();

this.message = errorInfo.getResultMsg();

}

public String getCode() {

return code;

}

public void setCode(String code) {

this.code = code;

}

public String getMessage() {

return message;

}

public void setMessage(String message) {

this.message = message;

}

public Object getResult() {

return result;

}

public void setResult(Object result) {

this.result = result;

}

/**

* 成功

*

* @return

*/

public static ResultBody success() {

return success(null);

}

/**

* 成功

* @param data

* @return

*/

public static ResultBody success(Object data) {

ResultBody rb = new ResultBody();

rb.setCode(CommonEnum.SUCCESS.getResultCode());

rb.setMessage(CommonEnum.SUCCESS.getResultMsg());

rb.setResult(data);

return rb;

}

/**

* 失败

*/

public static ResultBody error(BaseErrorInfoInterface errorInfo) {

ResultBody rb = new ResultBody();

rb.setCode(errorInfo.getResultCode());

rb.setMessage(errorInfo.getResultMsg());

rb.setResult(null);

return rb;

}

/**

* 失败

*/

public static ResultBody error(String code, String message) {

ResultBody rb = new ResultBody();

rb.setCode(code);

rb.setMessage(message);

rb.setResult(null);

return rb;

}

/**

* 失败

*/

public static ResultBody error( String message) {

ResultBody rb = new ResultBody();

rb.setCode("-1");

rb.setMessage(message);

rb.setResult(null);

return rb;

}

@Override

public String toString() {

return JSONObject.toJSONString(this);

}

}

自定义全局异常处理类

最后我们在来编写一个自定义全局异常处理的类。

代码如下:

@ControllerAdvice

public class GlobalExceptionHandler {

private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

/**

* 处理自定义的业务异常

* @param req

* @param e

* @return

*/

@ExceptionHandler(value = BizException.class)

@ResponseBody

public ResultBody bizExceptionHandler(HttpServletRequest req, BizException e){

logger.error("发生业务异常!原因是:{}",e.getErrorMsg());

return ResultBody.error(e.getErrorCode(),e.getErrorMsg());

}

/**

* 处理空指针的异常

* @param req

* @param e

* @return

*/

@ExceptionHandler(value =NullPointerException.class)

@ResponseBody

public ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e){

logger.error("发生空指针异常!原因是:",e);

return ResultBody.error(CommonEnum.BODY_NOT_MATCH);

}

/**

* 处理其他异常

* @param req

* @param e

* @return

*/

@ExceptionHandler(value =Exception.class)

@ResponseBody

public ResultBody exceptionHandler(HttpServletRequest req, Exception e){

logger.error("未知异常!原因是:",e);

return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);

}

}

因为这里我们只是用于做全局异常处理的功能实现以及测试,所以这里我们只需在添加一个实体类和一个控制层类即可。

实体类

又是万能的用户表 (▽)

代码如下:

public class User implements Serializable{

private static final long serialVersionUID = 1L;

/** 编号 */

private int id;

/** 姓名 */

private String name;

/** 年龄 */

private int age;

public User(){

}

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public String toString() {

return JSONObject.toJSONString(this);

}

}

Controller 控制层

控制层这边也比较简单,使用Restful风格实现的CRUD功能,不同的是这里我故意弄出了一些异常,好让这些异常被捕获到然后处理。这些异常中,有自定义的异常抛出,也有空指针的异常抛出,当然也有不可预知的异常抛出(这里我用类型转换异常代替),那么我们在完成代码编写之后,看看这些异常是否能够被捕获处理成功吧!

代码如下:

@RestController

@RequestMapping(value = "/api")

public class UserRestController {

@PostMapping("/user")

public boolean insert(@RequestBody User user) {

System.out.println("开始新增...");

//如果姓名为空就手动抛出一个自定义的异常!

if(user.getName()==null){

throw new BizException("-1","用户姓名不能为空!");

}

return true;

}

@PutMapping("/user")

public boolean update(@RequestBody User user) {

System.out.println("开始更新...");

//这里故意造成一个空指针的异常,并且不进行处理

String str=null;

str.equals("111");

return true;

}

@DeleteMapping("/user")

public boolean delete(@RequestBody User user) {

System.out.println("开始删除...");

//这里故意造成一个异常,并且不进行处理

Integer.parseInt("abc123");

return true;

}

@GetMapping("/user")

public List findByUser(User user) {

System.out.println("开始查询...");

List userList =new ArrayList<>();

User user2=new User();

user2.setId(1L);

user2.setName("xuwujing");

user2.setAge(18);

userList.add(user2);

return userList;

}

}

App 入口

和普通的SpringBoot项目基本一样。

代码如下:

@SpringBootApplication

public class App

{

public static void main( String[] args )

{

SpringApplication.run(App.class, args);

System.out.println("程序正在运行...");

}

}

功能测试

我们成功启动该程序之后,使用Postman工具来进行接口测试。

首先进行查询,查看程序正常运行是否ok,使用GET 方式进行请求。

返回参数为:

{"id":1,"name":"xuwujing","age":18}

示例图:

可以看到程序正常返回,并没有因自定义的全局异常而影响。

然后我们再来测试下自定义的异常是否能够被正确的捕获并处理。

使用POST方式进行请求

Body参数为:

{"id":1,"age":18}

返回参数为:

{"code":"-1","message":"用户姓名不能为空!","result":null}

示例图:

可以看出将我们抛出的异常进行数据封装,然后将异常返回出来。

然后我们再来测试下空指针异常是否能够被正确的捕获并处理。在自定义全局异常中,我们除了定义空指针的异常处理,也定义最高级别之一的Exception异常,那么这里发生了空指针异常之后,它是回优先使用哪一个呢?这里我们来测试下。

使用PUT方式进行请求。

Body参数为:

{"id":1,"age":18}

返回参数为:

{"code":"400","message":"请求的数据格式不符!","result":null}

示例图:

我们可以看到这里的的确是返回空指针的异常护理,可以得出全局异常处理优先处理子类的异常。

那么我们在来试试未指定其异常的处理,看该异常是否能够被捕获。

使用DELETE方式进行请求。

Body参数为:

{"id":1}

返回参数为:

{"code":"500","message":"服务器内部错误!","result":null}

这里可以看到它使用了我们在自定义全局异常处理类中的Exception异常处理的方法。

到这里,测试就结束了。顺便再说一下,自义定全局异常处理除了可以处理上述的数据格式之外,也可以处理页面的跳转,只需在新增的异常方法的返回处理上填写该跳转的路径并不使用ResponseBody 注解即可。 细心的同学也许发现了在GlobalExceptionHandler类中使用的是ControllerAdvice注解,而非RestControllerAdvice注解,如果是用的RestControllerAdvice注解,它会将数据自动转换成JSON格式,这种于Controller和RestController类似,所以我们在使用全局异常处理的之后可以进行灵活的选择处理。

其它

关于SpringBoot优雅的全局异常处理的文章就讲解到这里了,如有不妥,欢迎指正!

项目地址

SpringBoot整合系列的文章

音乐推荐

Spring Boot集成Activiti 7的过程中,如果你尝试通过监听器(如ExecutionListener、TaskListener等)去捕获Service Task内部的异常并通过BPMNError处理,可能会遇到一些挑战。首先,Activiti默认的行为是在Task完成后处理异常,这意味着在Service Task中直接抛出的异常不会立即触发监听器的回调。 通常,执行监听器更适用于生命周期事件,比如任务开始、结束,而TaskListener则用于任务执行过程中的一些特定操作,比如完成条件检查。若Service Task内部有预期之外的错误,应该在业务逻辑处理层面上捕获并转换为BPMNError,然后使用ThrowExceptionEvent来主动抛出。 以下是可能的做法: 1. **自定义拦截器**:你可以创建一个实现了DelegateExecutionAware或ExecutionListener接口的自定义类,在onExecute或onTaskCompleted方法中添加对Service Task异常的处理逻辑。 ```java @Service("myCustomTaskListener") public class MyCustomTaskListener implements ExecutionListener { @Override public void notify(DelegateExecution execution) { try { // 业务代码 } catch (Exception e) { execution.setVariable("customError", new ErrorInfo(e.getMessage())); throw new BPMNException("Custom Exception", "An error occurred in the service task", e); } } // 其他TaskListener方法... } ``` 2. **异常处理器**:在Activiti的流程引擎配置中设置一个全局异常处理器,它会在执行上下文捕获到异常时调用。 ```java @Bean public GlobalExceptionHandler exceptionHandler() { return new GlobalExceptionHandler() { @Override protected void handleException(DelegateExecution execution, Throwable t) { if (t instanceof BPMNException) { BPMNError error = new BPMNError(((BPMNException) t).getErrorCode(), ((BPMNException) t).getText()); execution.setVariable("customError", error); } super.handleException(execution, t); } }; } ``` 在这种情况下,如果想在Service Task中直接抛出异常,并希望在监听器中捕获,你需要确保异常是通过BPMNError的形式传递的,这样才能在监听器中被识别和处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值