SpringBoot如何编写统一异常管理呢?
下文笔者讲述使用AOP实现统一异常管理的方法分享,如下所示
结果集定义方式
实现思路: 1.引入AOP相应的jar包 2.编写相应的异常类 3.使用注解为编写exceptionhandler 4.编写AOP拦截所有错误信息
引入相应的starter器
<!--spring切面aop依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
定义返回报文的格式
返回报文包含以下信息:成功标志:是一个布尔值,用于表示是否运行正常 错误代码:使用整型作为标志位 错误信息:使用String作为错误信息的描述,留给前端是否展示给用户或者进入其他错误流程的使用。 结果集:在无错误信息的情况下所得到的正确数据信息。一般是个Map,前端根据Key取值例:
结果集定义方式
public class Result<T> { // error_code 状态值:0 极为成功,其他数值代表失败 private Integer status; // error_msg 错误信息,若status为0时,为success private String msg; // content 返回体报文的出参,使用泛型兼容不同的类型 private T data; public Integer getStatus() { return status; } public void setStatus(Integer code) { this.status = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData(Object object) { return data; } public void setData(T data) { this.data = data; } public T getData() { return data; } @Override public String toString() { return "Result{" + "status=" + status + ", msg='" + msg + '\'' + ", data=" + data + '}'; }
定义一个枚举类用于设置代码和信息的关联
public enum ExceptionEnum { UNKNOW_ERROR(-1,"未知错误"), USER_NOT_FIND(-101,"用户不存在"), ; private Integer code; private String msg; ExceptionEnum(Integer code, String msg) { this.code = code; this.msg = msg; } public Integer getCode() { return code; } public String getMsg() { return msg; } }
统一返回的结果集工具类
public class ResultUtil { /** * 返回成功,传入返回体具体出參 * @param object * @return */ public static Result success(Object object){ Result result = new Result(); result.setStatus(0); result.setMsg("success"); result.setData(object); return result; } /** * 提供给部分不需要出參的接口 * @return */ public static Result success(){ return success(null); } /** * 自定义错误信息 * @param code * @param msg * @return */ public static Result error(Integer code,String msg){ Result result = new Result(); result.setStatus(code); result.setMsg(msg); result.setData(null); return result; } /** * 返回异常信息,在已知的范围内 * @param exceptionEnum * @return */ public static Result error(ExceptionEnum exceptionEnum){ Result result = new Result(); result.setStatus(exceptionEnum.getCode()); result.setMsg(exceptionEnum.getMsg()); result.setData(null); return result; } }
异常类定义
public class DescribeException extends RuntimeException{ private Integer code; /** * 继承exception,加入错误状态值 * @param exceptionEnum */ public DescribeException(ExceptionEnum exceptionEnum) { super(exceptionEnum.getMsg()); this.code = exceptionEnum.getCode(); } /** * 自定义错误信息 * @param message * @param code */ public DescribeException(String message, Integer code) { super(message); this.code = code; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } }
异常处理类封装
@ControllerAdvice public class ExceptionHandle { private final static Logger LOGGER = LoggerFactory.getLogger(ExceptionHandle.class); /** * 判断错误是否是已定义的已知错误,不是则由未知错误代替,同时记录在log中 * @param e * @return */ @ExceptionHandler(value = Exception.class) @ResponseBody public Result exceptionGet(Exception e){ if(e instanceof DescribeException){ DescribeException MyException = (DescribeException) e; return ResultUtil.error(MyException.getCode(),MyException.getMessage()); } LOGGER.error("【系统异常】{}",e); return ResultUtil.error(ExceptionEnum.UNKNOW_ERROR); } }
注意事项: 1.此处使用@ControllerAdvice注解 则Spring会加载该类 并将所有捕获的异常统一返回结果Result这个实体 2.此处的操作方式可捕捉类的异常,但如果接口出现异常 则无法知道是谁调用了接口,所以此时我们需引入AOP
AOP捕捉异常
@Aspect @Component public class HttpAspect { private final static Logger LOGGER = LoggerFactory.getLogger(HttpAspect.class); @Autowired private ExceptionHandle exceptionHandle; @Pointcut("execution(public * com.java265.controller.*.*(..))") public void log(){ } @Before("log()") public void doBefore(JoinPoint joinPoint){ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); //url LOGGER.info("url={}",request.getRequestURL()); //method LOGGER.info("method={}",request.getMethod()); //ip LOGGER.info("id={}",request.getRemoteAddr()); //class_method LOGGER.info("class_method={}",joinPoint.getSignature().getDeclaringTypeName() + "," + joinPoint.getSignature().getName()); //args[] LOGGER.info("args={}",joinPoint.getArgs()); } @Around("log()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Result result = null; try { } catch (Exception e) { return exceptionHandle.exceptionGet(e); } if(result == null){ return proceedingJoinPoint.proceed(); }else { return result; } } @AfterReturning(pointcut = "log()",returning = "object")//打印输出结果 public void doAfterReturing(Object object){ LOGGER.info("response={}",object.toString()); } }
以上代码将使用@Aspect来声明这是一个切面 使用@Pointcut来定义切面所需要切入的位置 此处AOP将对HTTP请求做一个切入 在进入方法之前我们使用@Before记录了调用的接口URL,调用的方法,调用方的IP地址以及输入的参数等 在整个接口代码运作期间 我们使用@Around来捕获异常信息,并用之前定义好的Result进行异常的返回 最后我们使用@AfterReturning来记录我们的出參。 通过以上方法即可实现日志监控例:
Controller示例编写
@RestController @RequestMapping("/result") public class ResultController { @Autowired private ExceptionHandle exceptionHandle; /** * 返回体测试 * @param name * @param pwd * @return */ @RequestMapping(value = "/getResult",method = RequestMethod.POST) public Result getResult(@RequestParam("name") String name, @RequestParam("pwd") String pwd){ Result result = ResultUtil.success(); try { if (name.equals("maomaojava265")){ result = ResultUtil.success(new UserInfo()); }else if (name.equals("pzz")){ result = ResultUtil.error(ExceptionEnum.USER_NOT_FIND); }else{ int i = 1/0; } }catch (Exception e){ result = exceptionHandle.exceptionGet(e); } return result; } }
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。