前言
在 Controller 里提供接口,通常需要捕捉异常,进行异常处理。最简单的方法使用try/catch进行异常捕捉。
当方法很多,每个都需要 try catch,代码会显得臃肿,写起来也比较麻烦。
这时就需要进行统一的异常处理。
1.使用方法
通过 Spring 的 AOP 特性就可以很方便的实现异常的统一处理:使用@ControllerAdvice、@RestControllerAdvice捕获运行时异常。
代码结构
新建异常枚举类
package com.local.dev.root.devroot.common.enums;
/**
* 异常枚举类
*/
public enum ExceptionEnum {
// 400
BAD_REQUEST("400", "请求数据格式不正确!"),
UNAUTHORIZED("401", "登录凭证过期!"),
FORBIDDEN("403", "没有访问权限!"),
NOT_FOUND("404", "请求的资源找不到!"),
// 500
INTERNAL_SERVER_ERROR("500", "服务器内部错误!"),
SERVICE_UNAVAILABLE("503", "服务器正忙,请稍后再试!"),
// 未知异常
UNKNOWN("10000", "未知异常!"),
// 自定义
IS_NOT_NULL("10001","%s不能为空");
/**
* 错误码
*/
private String code;
/**
* 错误描述
*/
private String msg;
ExceptionEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
自定义异常类
package com.local.dev.root.devroot.common.exception;
import com.local.dev.root.devroot.common.enums.ExceptionEnum;
/**
* 自定义业务异常类
*/
public class BusinessException extends RuntimeException {
private ExceptionEnum exceptionEnum;
private String code;
private String errorMsg;
public BusinessException() {
super();
}
public BusinessException(ExceptionEnum exceptionEnum) {
super("{code:" + exceptionEnum.getCode() + ",errorMsg:" + exceptionEnum.getMsg() + "}");
this.exceptionEnum = exceptionEnum;
this.code = exceptionEnum.getCode();
this.errorMsg = exceptionEnum.getMsg();
}
public BusinessException(String code, String errorMsg) {
super("{code:" + code + ",errorMsg:" + errorMsg + "}");
this.code = code;
this.errorMsg = errorMsg;
}
public BusinessException(String code, String errorMsg, Object... args) {
super("{code:" + code + ",errorMsg:" + String.format(errorMsg, args) + "}");
this.code = code;
this.errorMsg = String.format(errorMsg, args);
}
public ExceptionEnum getExceptionEnum() {
return exceptionEnum;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
ExceptionHandlerConfig.java
@RestControllerAdvice,统一异常处理
package com.local.dev.root.devroot.common.config.exception;
import com.local.dev.root.devroot.common.enums.ExceptionEnum;
import com.local.dev.root.devroot.common.exception.BusinessException;
import com.local.dev.root.devroot.common.exception.ErrorPageException;
import com.local.dev.root.devroot.common.pojo.ApiResponse;
import com.local.dev.root.devroot.common.util.ErrorUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* RestControllerAdvice,统一异常处理
*/
@Slf4j
@RestControllerAdvice
public class ExceptionHandlerConfig {
/**
* 业务异常处理
*
* @param e 业务异常
* @return
*/
@ExceptionHandler(value = BusinessException.class)
@ResponseBody
public ApiResponse exceptionHandler(BusinessException e) {
log.error(ErrorUtil.errorInfoToString(e));
return ApiResponse.error(e.getCode(), e.getErrorMsg());
}
/**
* 未知异常处理
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ApiResponse exceptionHandler(Exception e) {
// 把错误信息输入到日志中
log.error(ErrorUtil.errorInfoToString(e));
return ApiResponse.error(ExceptionEnum.UNKNOWN.getCode(),
ExceptionEnum.UNKNOWN.getMsg());
}
/**
* 错误页面异常
*/
@ExceptionHandler(value = ErrorPageException.class)
@ResponseBody
public ApiResponse exceptionHandler(ErrorPageException e) {
log.error(ErrorUtil.errorInfoToString(e));
return ApiResponse.error(e.getCode(), e.getErrorMsg());
}
/**
* 空指针异常
*/
@ExceptionHandler(value = NullPointerException.class)
@ResponseBody
public ApiResponse exceptionHandler(NullPointerException e) {
log.error(ErrorUtil.errorInfoToString(e));
return ApiResponse.error(ExceptionEnum.INTERNAL_SERVER_ERROR.getCode(),
ExceptionEnum.INTERNAL_SERVER_ERROR.getMsg());
}
}
测试类
package com.local.dev.root.devroot.controller;
import com.local.dev.root.devroot.common.pojo.ApiResponse;
import com.local.dev.root.devroot.service.dev.TestServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/hello")
public class HelloWorld {
@Autowired
private TestServiceImpl testServiceImpl;
@RequestMapping("world")
public String HelloWorld() {
return "Hello World!";
}
@RequestMapping("error1")
public ApiResponse Error1() {
return ApiResponse.ok();
}
@RequestMapping("error2")
public ApiResponse Error2() {
String msg = null;
msg.equals("xx");
return ApiResponse.ok("访问成功");
}
@RequestMapping("error3")
public ApiResponse Error3() {
testServiceImpl.getBusinessException();
return ApiResponse.ok("访问成功");
}
}
testServiceImpl 抛出自定义异常
package com.local.dev.root.devroot.service.dev;
import com.local.dev.root.devroot.common.enums.ExceptionEnum;
import com.local.dev.root.devroot.common.exception.BusinessException;
import org.springframework.stereotype.Service;
@Service
public class TestServiceImpl {
public Object getBusinessException() {
throw new BusinessException(ExceptionEnum.IS_NOT_NULL.getCode(),
ExceptionEnum.IS_NOT_NULL.getMsg(), "参数");
}
}
ErrorUtil工具类
package com.local.springboot.springbootcommon.utils;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* 捕获报错日志处理工具类
*/
public class ErrorUtil {
/**
* Exception出错的栈信息转成字符串
* 用于打印到日志中
*/
public static String errorInfoToString(Throwable e) {
//try-with-resource语法糖 处理机制
try(StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)){
e.printStackTrace(pw);
pw.flush();
sw.flush();
return sw.toString();
}catch (Exception ignored){
throw new RuntimeException(ignored.getMessage(),ignored);
}
}
}
ApiResponse
package com.local.springboot.springbootcommon.reponse;
import java.util.HashMap;
/**
* @author: MaoDeShu
* @date: 2021-09-17 11:29
* @Description: API请求返回类
**/
public class ApiResponse extends HashMap {
public ApiResponse() {
put("code", 200);
put("msg", "success");
}
public ApiResponse(String code, String msg) {
super(2);
put("code", code);
put("msg", msg);
}
public static ApiResponse ok() {
ApiResponse r = new ApiResponse();
r.put("msg", "操作成功");
return r;
}
public static ApiResponse msg(String msg) {
ApiResponse r = new ApiResponse();
r.put("msg", msg);
return r;
}
public static ApiResponse ok(Object obj) {
ApiResponse r = new ApiResponse();
r.put("code", 200);
r.put("results", obj);
return r;
}
public static ApiResponse error() {
return error("500", "未知异常,请联系管理员");
}
public static ApiResponse error(String msg) {
return error("500", msg);
}
public static ApiResponse error(String code, String msg) {
ApiResponse r = new ApiResponse();
r.put("code", code);
r.put("msg", msg);
return r;
}
@Override
public ApiResponse put(String key, Object value) {
super.put(key, value);
return this;
}
/**
* 分页
*
* @param page
* @return
*/
public ApiResponse page(Object page) {
return put("page", page);
}
public ApiResponse result(Object obj) {
super.put("result", obj);
return this;
}
}
2.测试
正常
http://localhost:8080/hello/error1
空指针异常
http://localhost:8080/hello/error2
业务异常
http://localhost:8080/hello/error3
3.查看日志
打开error日志
« 上一章:SpringBoot —— 多线程定时任务的实现(注解配置、task:annotation-driven配置)
» 下一章:SpringBoot —— 简单整合Redis实例及StringRedisTemplate与RedisTemplate对比和选择
创作不易,关注、点赞就是对作者最大的鼓励,欢迎在下方评论留言
求关注,定期分享Java知识,一起学习,共同成长。