说明:在完整的项目结构中,我们通常会创建一个自定义的异常处理机制,在系统可能出现异常的地方手动抛出这些异常,可以快速定位到异常代码片段,提供项目的可维护性。
本文介绍在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 {@Autowiredprivate 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 {@Overridepublic 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);}@Overridepublic 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 {@Overridepublic 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);}@Overridepublic 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的异常处理注解,可以实现对项目量身定做一套异常处理机制,根据项目中出现的异常,来返回对应的异常信息,非常方便。