全局异常处理实战
1445字约5分钟
2024-06-25
开始前的准备
1、依赖引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2、结果响应类:ResultDTO
ResultDTO
是封装的通用结果类,详解请移步 封装通用结果类
具体异常处理
@ControllerAdvice
是在类上声明的注解,配合 @ExceptionHandler
能灵活地对不同的异常进行分别的处理。当抛出的异常是指定异常的子类,那么照样能够被捕获和处理。
这里我们以 ArithmeticException
为例进行代码演示。
package com.marui.exception.handle.common.exception;
import com.marui.exception.handle.common.ResultDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* @ClassName AppExceptionHandler
* @Desciption 全局统一异常处理器
* @Author MaRui
* @Date 2023/2/24 11:01
* @Version 1.0
*/
@RestControllerAdvice
public class AppExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 处理算术运算异常
*/
@ExceptionHandler(ArithmeticException.class)
public ResultDTO handleException(ArithmeticException e) {
logger.error(e.getMessage(), e);
return ResultDTO.error("500", "这是处理的算术运算异常呀");
}
@ExceptionHandler(Exception.class)
public ResultDTO handleException(Exception e) {
logger.error(e.getMessage(), e);
return ResultDTO.error("500", "未知错误,请联系系统管理员");
}
}
/**
* @ClassName MessageController
* @Desciption 消息控制层
* @Author MaRui
* @Date 2023/2/24 11:08
* @Version 1.0
*/
@Data
@RestController
@RequestMapping(value = "/message")
public class MessageController {
@GetMapping(value = "/getMessage")
public ResultDTO getMessage() {
int a = 1/0;
return ResultDTO.ok();
}
}
页面访问效果
自定义异常
自定义异常类只需从 Exception
类或者它的子类派生一个子类即可。自定义异常类如果继承 Exception
类,则为受检查异常,必须对其进行处理;如果不想处理,可以让自定义异常类继承运行时异常 RuntimeException
类。习惯上,自定义异常类应该包含 2
个构造器:一个是默认的构造器,另一个是带有详细信息的构造器。
自定义异常
package com.marui.exception.handle.common.exception;
/**
* @ClassName AppException
* @Desciption 自定义异常类
* @Author MaRui
* @Date 2023/2/24 10:59
* @Version 1.0
*/
public class AppException extends RuntimeException {
private String msg;
private String code = "500";
public AppException(String msg) {
super(msg);
this.msg = msg;
}
public AppException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public AppException(String msg, String code) {
super(msg);
this.msg = msg;
this.code = code;
}
public AppException(String msg, String code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String toString() {
return "AppException{" + "msg='" + msg + '\'' + ", code='" + code + '\'' + '}';
}
public String toJson() {
return "{" + "msg='" + msg + '\'' + ", code='" + code + '\'' + '}';
}
}
全局异常处理器中增加对自定义异常的处理
package com.marui.exception.handle.common.exception;
import com.marui.exception.handle.common.ResultDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* @ClassName AppExceptionHandler
* @Desciption 全局统一异常处理器
* @Author MaRui
* @Date 2023/2/24 11:01
* @Version 1.0
*/
@RestControllerAdvice
public class AppExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 处理自定义异常
*/
@ExceptionHandler(AppException.class)
public ResultDTO handleException(AppException e) {
logger.error(e.getMessage(), e);
return ResultDTO.error(e.getCode(), e.getMsg());
}
@ExceptionHandler(Exception.class)
public ResultDTO handleException(Exception e) {
logger.error(e.getMessage(), e);
return ResultDTO.error("500", "未知错误,请联系系统管理员");
}
}
/**
* @ClassName MessageController
* @Desciption 消息控制层
* @Author MaRui
* @Date 2023/2/24 11:08
* @Version 1.0
*/
@Data
@RestController
@RequestMapping(value = "/message")
public class MessageController {
@GetMapping(value = "/getMessage")
public ResultDTO getMessage() {
throw new AppException("抛出一个小异常");
}
}
页面上请求效果图
优雅的抛出异常
在开发中我们经常会写一个 if
判断,如果满足/不满足条件则通过 throw new Exception
的方式抛出异常,如果一两个这样的 if
可能还能接受,如果多了就显得业务代码比较多、乱。我们通过编写一个工具类 ConditionUtil
来帮助我们将异常抛出,而且可以将一部分判断逻辑交给 ConditionUtil
,还可以根据需要自定义更多符合业务场景的条件判断方法。
异常枚举值,可以统一定义需要抛出的业务异常值
异常枚举值
package com.marui.exception.handle.common.exception;
/**
* @Enum ResCode
* @Desciption 异常枚举值
* @Author MaRui
* @Date 2023/2/27 14:17
* @Version 1.0
*/
public enum ResCode {
PARAM_VALID("0000","参数校验"),
ARGUMENT_EMPTY("010001","参数不可以为空");
private final String code;
private final String value;
ResCode(String code, String value) {
this.code = code;
this.value = value;
}
public String getCode() {
return this.code;
}
public String getValue() {
return this.value;
}
/**
* 根据code获取value
*
* @param code
* @return
*/
public static String getValue(String code) {
for (ResCode r : ResCode.values()) {
if (r.getCode().equals(code)) {
return r.value;
}
}
return null;
}
}
ConditionUtil
工具类,定义异常抛出方法。
ConditionUtil 工具类
package com.marui.exception.handle.util;
import com.marui.exception.handle.common.exception.AppException;
import com.marui.exception.handle.common.exception.ResCode;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
/**
* @ClassName ConditionUtil
* @Desciption 条件判断抛出异常工具
* @Author MaRui
* @Date 2023/2/27 14:15
* @Version 1.0
*/
public class ConditionUtil {
/**
* 校验参数非空:如果对象为 null 抛出自定义异常
* @param object
* @param msg
*/
public static void checkNotNull(Object object, String msg) {
if (null == object) {
throw new AppException(ResCode.PARAM_VALID.getValue() + ": " + msg, ResCode.PARAM_VALID.getCode());
}
}
/**
* 校验参数非空:如果对象为 null 抛出自定义异常
* @param object
* @param resCode
*/
public static void checkNotNull(Object object, ResCode resCode) {
if (null == object) {
throw new AppException(resCode.getValue(), resCode.getCode());
}
}
/**
* 由调用方校验,将校验结果判断是否抛出自定义异常
* @param bo
* @param msg
*/
public static void checkArgument(Boolean bo, String msg) {
if (!bo) {
throw new AppException(ResCode.PARAM_VALID.getValue() + ": " + msg, ResCode.PARAM_VALID.getCode());
}
}
/**
* 由调用方校验,将校验结果判断是否抛出自定义异常
* @param bo
* @param resCode
*/
public static void checkArgument(Boolean bo, ResCode resCode) {
if (!bo) {
throw new AppException(resCode.getValue(), resCode.getCode());
}
}
/**
* 集合不为空:如果集合为空抛出自定义异常
* @param collection
* @param msg
*/
public static void checkCollectionNotEmpty(Collection<?> collection, String msg) {
if (CollectionUtils.isEmpty(collection)) {
throw new AppException(ResCode.PARAM_VALID.getValue() + ": " + msg, ResCode.PARAM_VALID.getCode());
}
}
/**
* 集合不为空:如果集合为空抛出自定义异常
* @param collection
* @param resCode
*/
public static void checkCollectionNotEmpty(Collection<?> collection, ResCode resCode) {
if (CollectionUtils.isEmpty(collection)) {
throw new AppException(resCode.getValue(), resCode.getCode());
}
}
}
用于测试的接口,根据测试需要注释掉不同的 ConditionUtil
方法调用
@Data
@RestController
@RequestMapping(value = "/message")
public class MessageController {
@GetMapping(value = "/getMessage")
public ResultDTO getMessage() {
// 校验参数不为null。结果:{"code":"010001","msg":"参数不可以为空","data":null}
ConditionUtil.checkNotNull(null, ResCode.ARGUMENT_EMPTY);
// 校验数据。结果:{"code":"0000","msg":"参数校验: 参数为false抛出异常","data":null}
ConditionUtil.checkArgument("a".equals("b"), "参数为false抛出异常");
// 校验集合。结果:{"code":"0000","msg":"参数校验: 集合为空","data":null}
ConditionUtil.checkCollectionNotEmpty(new ArrayList<>(), "集合为空");
return ResultDTO.ok();
}
}