第一种:
1. 我们将采用Spring AOP统一处理异常,统一返回后端接口的结果。
2. 使用一个自定义异常和一个错误前端提示枚举来逐层传递消息
3. 一个错误枚举来代替新建异常信息类,减少业务异常信息文件的数量
//正常返回的枚举 SUCCESS(true, 2000,"正常返回", "操作成功"), // 系统错误,50开头 SYS_ERROR(false, 5000, "系统错误", "亲,系统出错了哦~"), PARAM_INVILAD(false, 5001, "参数出现异常", "参数出现异常"), DATA_NO_COMPLETE(false, 5002, "数据填写不完整,请检查", "数据填写不完整,请检查"); private ErrorMsgEnum(boolean ok, int code, String msg ,String userMsg) { this.ok = ok; this.code = code; this.msg = msg; this.userMsg = userMsg; } private boolean ok; private int code; private String msg; private String userMsg; } |
控制层返回结果POJO类
public class JsonResponse{ String msg; Object data; public JsonResponse() { msg = ""; data = null; } public static JsonResponse newOk() { JsonResponse response = new JsonResponse(); response.setState(State.newOk()); return response; } public static JsonResponse newOk(Object data) { JsonResponse response = new JsonResponse(); response.setData(data); response.setState(State.newOk()); return response; } public static JsonResponse newError() { JsonResponse response = new JsonResponse(); response.setMsg("无情的系统异常!"); return response; } public static JsonResponse newError(ErrorMsgEnum errorMsgEnum) { JsonResponse response = new JsonResponse(); state.setMsg(errorMsgEnum.getErrorMsg()); return response; } } |
自定义异常类
public class CustomException extends Exception { private ErrorMsgEnum errorMsgEnum; public CustomException(ErrorMsgEnum errorMsgEnum) { this.errorMsgEnum = errorMsgEnum; } } |
AOP捕获异常处理类
@Around("execution(public * com.jason.*.controller..*.*(..))") public JsonResponse serviceAOP(ProceedingJoinPoint pjp) throws Exception { JsonResponse newResultVo = null; try { return (JsonResponse) pjp.proceed(); } catch (CustomException e) { logger.info("自定义业务异常:" + e.getMessage()); ErrorMsgEnum errorMsgEnum = e.getErrorMsgEnum(); if (Objects.nonNull(errorMsgEnum)) { newResultVo = JsonResponse.newError(errorMsgEnum); } else { newResultVo = JsonResponse.newError(e.getMessage()); } } catch (Exception e) { //可以顺便处理你的日志,此处能取到方法名,参数等等 logger.error("出现运行时异常:", e); newResultVo = JsonResponse.newError(); } return newResultVo; } |
第二种
在springboot中使用aop 来处理异常
1. 在SpringBoot中引入AOP
2. 创建一个返回体报文的实体类
public class Result // 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 + '}'; } |
3. 创建一个枚举类,来记录一些我们已知的错误信息,可以在代码中直接使用。
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; } } |
4. 创建一个工具类在代码中使用
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; } } |
5. 一般系统抛出的错误是不含错误代码的,除去部分的404,400,500错误之外,我们如果想把错误代码定义的更细致,就需要自己继承RuntimeException这个类后重新定义一个构造方法来定义我们自己的错误信息:
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; } } |
6. 使用一个Handle来把Try,Catch中捕获的错误进行判定,是一个我们已知的错误信息,还是一个未知的错误信息,如果是未知的错误信息,那我们就用log记录它,便于之后的查找和解决:
@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); } } |
7. 我们使用接口若出现了异常,很难知道是谁调用接口,是前端还是后端出现的问题导致异常的出现,那这时,AOP久发挥作用了,我们之前已经引入了AOP的依赖,现在我们编写一个切面类
@Aspect @Component public class HttpAspect { private final static Logger LOGGER = LoggerFactory.getLogger(HttpAspect.class); @Autowired private ExceptionHandle exceptionHandle; @Pointcut("execution(public * com.zzp.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()); } } |
8. 我们使用@Aspect来声明这是一个切面,使用@Pointcut来定义切面所需要切入的位置,这里我们是对每一个HTTP请求都需要切入,在进入方法之前我们使用@Before记录了调用的接口URL,调用的方法,调用方的IP地址以及输入的参数等。在整个接口代码运作期间,我们使用@Around来捕获异常信息,并用之前定义好的Result进行异常的返回,最后我们使用@AfterReturning来记录我们的出參。
以上全部,我们就完成了异常的统一管理以及切面获取接口信息
@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("zzp")){ 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; } } |
结果:
第三种:基于xml 配置的aop
上图中的接口上没有任何方法,但是实现了这个接口的类必须至少实现以下4个方法中的一个,否则程序报:
Caused by: java.lang.IllegalArgumentException: At least one handler method must be found in class
接口中的异常类可以为自己自定义的异常类,方法是通过反射调用。
1. 自定义异常类
package com.apt.study.exception; public class StudyException extends RuntimeException { /** /** public StudyException(Throwable cause) { public StudyException(String message) { public String getMessage() { public void setMessage(String message) { public int getCode() { public void setCode(int code) { |
2. 异常日志处理类
package com.apt.study.exception; import java.lang.reflect.Method; import org.slf4j.Logger; public class ExceptionLogHandler implements ThrowsAdvice{ public Logger logger = LoggerFactory.getLogger(ExceptionLogHandler.class); |
3. spring-context.xml 配置