说明:在完整的项目结构中,我们通常会创建一套自定义的异常处理机制,在系统可能出现异常的地方手动抛出这些异常,可以快速定位到异常代码片段,提高系统的可维护性。
本文介绍在SpringBoot项目中,搭建一套自定义异常处理机制。
统一响应结果
首先,创建一个统一响应结果类,如下,用于统一后台的返回结果,请求成功返回数据,请求失败返回错误信息;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 后端统一返回结果
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 编码:1成功 0和其他数字为失败
*/
private Integer code;
/**
* 错误信息
*/
private String msg;
/**
* 数据
*/
private T data;
/**
* 成功并返回
*
* @param <T>
* @return
*/
public static <T> Result<T> success() {
Result<T> result = new Result<T>();
result.code = 1;
return result;
}
/**
* 成功并返回数据
*
* @param object
* @param <T>
* @return
*/
public static <T> Result<T> success(T object) {
Result<T> result = new Result<T>();
result.data = object;
result.code = 1;
return result;
}
/**
* 失败,并返回信息
*
* @param msg
* @param <T>
* @return
*/
public static <T> Result<T> error(String msg) {
Result result = new Result();
result.msg = msg;
result.code = 0;
return result;
}
}
然后,创建两个接口,用于用户登录,根据ID查找用户,如下:
(controller层)
import com.hezy.pojo.entity.User;
import com.hezy.pojo.vo.Result;
import com.hezy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/exception")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/login")
public Result login(@RequestBody User user){
return userService.login(user);
}
@GetMapping("/get")
public Result get(Integer id){
return userService.getUser(id);
}
}
(Service实现类,其中TODO表示待完成的部分)
import cn.hutool.core.util.ObjUtil;
import com.hezy.pojo.entity.User;
import com.hezy.pojo.vo.Result;
import com.hezy.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Override
public Result login(User user) {
// 对user进行校验
if (!ObjUtil.isAllNotEmpty(user, user.getUsername(), user.getPassword())) {
return Result.error("非法参数异常");
}
// TODO 根据用户名查询数据库,校验该用户是否存在
if (true) {
return Result.error("该用户不存在");
}
// TODO 保存用户信息到数据库
return Result.success(user);
}
@Override
public Result getUser(Integer id) {
// 对id进行校验
if (id == null || id < 0) {
return Result.error("非法参数异常");
}
// TODO 根据用户名查询数据库, 校验用户是否存在
if (true) {
return Result.error("该用户不存在");
}
return Result.success(new User(id, "张三", "123456"));
}
}
(用户实体类)
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
private Integer id;
/**
* 账号
*/
private String username;
/**
* 密码
*/
private String password;
}
启动项目,运行;
(测试登录接口,因为在if里面写死了,所以返回错误信息)
(测试获取用户对象接口,因为在if里面写死了,所以返回错误信息)
异常枚举
接下来,对上面的异常进行统一处理。
首先,创建一个异常的枚举类,里面包含了异常状态码,异常信息等,如下:
/**
* 异常枚举类
*/
public enum ExceptionEnum {
PARAM_EXCEPTION(10001, "非法参数异常"),
PARAM_LACK(10002, "参数不全异常"),
UNKNOW_EXCEPTION(99999, "未知异常");
private int status;
private String message;
ExceptionEnum(int status, String message) {
this.status = status;
this.message = message;
}
public int getStatus() {
return status;
}
public String getMessage() {
return message;
}
}
自定义异常
创建一个自定义异常类,继承RuntimeException类,如下:
import com.hezy.enums.ExceptionEnum;
/**
* 自定义异常
*/
public class MyException extends RuntimeException{
/**
* 异常枚举
*/
private ExceptionEnum exceptionEnum;
public MyException(ExceptionEnum exceptionEnum) {
super(exceptionEnum.getMessage());
this.exceptionEnum = exceptionEnum;
}
public ExceptionEnum getExceptionEnum() {
return exceptionEnum;
}
/**
* 手动抛出异常
* @param exceptionEnum
* @return
*/
public static MyException throwException(ExceptionEnum exceptionEnum){
return new MyException(exceptionEnum);
}
}
再创建一个异常的controller,自定义捕获对应的异常,并打印信息;
import com.hezy.enums.ExceptionEnum;
import com.hezy.pojo.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器
*/
@RestControllerAdvice
@Slf4j
public class GlobalException {
/**
* 自定义异常
* @param e
* @return
*/
@ExceptionHandler(MyException.class)
public Result handleException(MyException e){
log.error(e.getMessage());
return Result.builder()
.code(0)
.msg(e.getExceptionEnum().getMessage())
.build();
}
/**
* 全局异常
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
public Result handleException(Exception e){
log.error(e.getMessage());
return Result.builder()
.code(0)
.msg(ExceptionEnum.UNKNOW_EXCEPTION.getMessage())
.build();
}
}
最后,对前面业务实现类里面的代码进行修改,当参数校验错误时,直接抛出异常,如下:
import cn.hutool.core.util.ObjUtil;
import com.hezy.enums.ExceptionEnum;
import com.hezy.exception.MyException;
import com.hezy.pojo.entity.User;
import com.hezy.pojo.vo.Result;
import com.hezy.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Override
public Result login(User user) {
// 对user进行校验,其中user、username、password,都不能为空
if (!ObjUtil.isAllNotEmpty(user, user.getUsername(), user.getPassword())) {
throw new MyException(ExceptionEnum.PARAM_LACK);
}
// TODO 根据用户名查询数据库,校验该用户是否存在
if (true) {
return Result.error("该用户不存在");
}
// TODO 保存用户信息到数据库
return Result.success(user);
}
@Override
public Result getUser(Integer id) {
// id == null,参数不全异常
if (id == null) {
throw new MyException(ExceptionEnum.PARAM_LACK);
}
// id < 0,非法参数异常
if (id < 0) {
throw new MyException(ExceptionEnum.PARAM_EXCEPTION);
}
// TODO 根据用户名查询数据库, 校验用户是否存在
if (true) {
return Result.error("该用户不存在");
}
return Result.success(new User(id, "张三", "123456"));
}
}
启动项目,测试;
(当user中的username为空时)
(当获取对象的id < 0时)
当发生其他未知异常时,如post请求的接口,我们使用get方式发送时;
就会被全局异常捕获到,并在控制台打印报错信息;
如果此时,需要新增一个异常,只需要在ExceptionEnum(异常枚举类)中新增一个枚举项即可,如下:
(新增一个登录异常)
/**
* 异常枚举类
*/
public enum ExceptionEnum {
PARAM_EXCEPTION(10001, "非法参数异常"),
PARAM_LACK(10002, "参数不全异常"),
LOGIN_EXCEPTION(10003, "登录异常"),
UNKNOW_EXCEPTION(99999, "未知异常");
private int status;
private String message;
ExceptionEnum(int status, String message) {
this.status = status;
this.message = message;
}
public int getStatus() {
return status;
}
public String getMessage() {
return message;
}
}
总结
通过异常枚举类 + 自定义异常类 + SpringBoot的异常处理注解,可以实现对项目量身定做一套异常处理机制,根据项目中出现的异常,来返回对应的异常信息,非常方便。