Spring MVC统一异常处理
一、为什么需要统一异常处理?
1.1 try catch带来的问题
-
平时项目中处理
Controller
层异常很多时候都会使用try catch
去捕获异常(包括Controller
层中调用的Service
层中的try catch
)。 -
这样处理异常,会造成代码中带有大量的
try catch
块,不但代码不美观不优雅,严重的还会造成很大的性能问题。 -
这时候可能有人会说用
SpringAOP
定义切面去处理所有Controller
,然后有异常的时候统一拦截起来。 -
但是这种呆瓜操作,
Spring MVC
早就给我们想好了,无需我们自己取定义。
1.2 最佳解决方案
Spring MVC
给我们提供了一个@RestControllerAdvice
来增强所有@RestController
,然后使用@ExceptionHandler
注解,就可以拦截到对应的异常。
二、使用Spring MVC的统一异常处理机制
2.1 和前端约定好的通用返回结果类
这一步是非必须,但是项目中一般都会约定好一个通用的消息返回格式。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 通用返回结果类
* @param <T> 通用泛型(传了这个泛型的话, 数据data的类型就和这个泛型一致)
* 这个T可以理解为type类型。
* 使用其它字母也行,但是最好能保持一定的语义化,让大家更容易理解。
*/
@Data //lombok注解 提供get/set、toString、equal等实体类都有的方法
@NoArgsConstructor //lombok注解 提供无参构造
@AllArgsConstructor //lombok注解 提供全参构造
public class ReturnMessage<T> implements Serializable {
private Integer code; //返回状态码
private String msg; //错误信息
private T data; //数据
/**
* 创建一个要返回的消息对象
* @param code 状态码
* @param msg 错误信息
* @param data 数据
* @return
* @param <T> 声明这个静态方法是一个泛型方法
*/
public static <T> ReturnMessage<T> create(Integer code, String msg, T data) {
ReturnMessage<T> r = new ReturnMessage<T>(code, msg, data);
return r;
}
}
2.2 定义响应码枚举类
项目中通常会约定好返回给前端的状态码,为了代码得优雅和可维护性,可以使用一个枚举类来管理
import lombok.Getter;
@Getter //lombok注解 提供各属性的get方法
public enum AppCode {
SUCCESS(200, "成功"),
SERVICE_ERROR(500, "服务器异常"),
PASSWORD_ERROR(5001, "密码错误"),
CHECK_ERROR(5002, "验证码无效");
private int code;
private String msg;
AppCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
}
2.3 自定义业务异常类
import com.tanhua.model.vo.ErrorResult;
import lombok.Data;
@Data
public class BusinessException extends RuntimeException{
private AppCode appCode; //异常状态码对象
public BusinessException(AppCode appCode) {
super(appCode.getMsg());
this.appCode = appCode;
}
}
2.4 自定义统一异常处理类
import com.tanhua.model.vo.ErrorResult;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* 自定义统一异常处理类
* 步骤:
* 1、通过注解,声明异常处理类
* 2、编写方法,在方法内部处理异常,构造响应数据
* 3、方法上编写注解@ExceptionHandler,指定此方法可以处理的异常类型
*
* 使用Spring推荐的ResponseEntity作为返回值时:
* 1. 可以手动修改返回的状态码和响应体
* 2. 此类不需要加@ResponseBody
*
* 如果是自定义的返回结果类作为返回值时:
* 1.比如直接使用上面的ReturnMessage作为返回值, 则需要在此类或者方法上添加@ResponseBody
* 2.此时可以使用@RestControllerAdvice注解, 它等同于ControllerAdvice + @ResponseBody
*/
@ControllerAdvice
public class ExceptionAdvice {
//处理业务异常
@ExceptionHandler(BusinessException.class)
public ResponseEntity handlerBusinessException(BusinessException be) {
be.printStackTrace();
AppCode appCode = be.getAppCode();
ReturnMessage returnMessage = new ReturnMessage(
appCode.getCode(),
appCode.getMsg(),
null);
return ResponseEntity.status(appCode.getCode())
.body(returnMessage);
}
//处理不可预知的异常 (像什么空指针、数组下标越界、类型转换等非业务异常)
@ExceptionHandler(Exception.class)
public ResponseEntity handlerException(Exception e) {
e.printStackTrace();
ReturnMessage returnMessage = new ReturnMessage(
AppCode.SERVICE_ERROR.getCode(),
e.getMessage(),
null);
/**
* 拓展:
* HttpStatus 是Spring自带的状态码信息类
* 源码中的定义 :
* INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
* 可以看到 INTERNAL_SERVER_ERROR代表的就是500状态码,服务端异常
* 这种写法语义性更强,且不用自己再去定义
*
* 但是我这里并没有使用,而是选择了自定义状态码
*/
return ResponseEntity.status(AppCode.SERVICE_ERROR.getCode())
.body(returnMessage);
}
}
2.5 遇到业务异常时的处理示例:
public Boolean loginVerification(String phone, String password) {
User user = userService.findByPhone(phone);
if(user.getPassword != password) {
//密码不相等,抛出自定义业务异常
throw new BusinessException(ErrorResult.passswordError());
return false;
}
return true;
}
2.6 统一异常异常执行原理
-
定义好了统一异常处理,再遇到业务异常,可以直接
throw new BusinessException()
的方式抛出异常 -
此时我们定义的
ExceptionAdvice
类就会根据handlerBusinessException
方法去构建错误信息返回给前端。 -
而其它项目中的空指针、数组下标越界、类型转换等非业务异常,交由
handlerException
处理。这样就实现了项目中的统一异常处理。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/116508.html