欢迎关注公众号:其实是白羊。干货持续更新中......
废话不多说,先来几个基础垫吧垫吧。
顾名思义,异常就是不正常呗,这是一种现象,也是Java为我们提供的程序安全退出的通道。一旦出现异常,异常处理机制会将代码执行交给异常处理器,而不再执行原有方法。
为了描述不同的这种不正常现象,我们定义了各种各样的异常类型。
首先来张图:
没错它属所有异常和错误的“祖宗”,下面介绍几个常用的方法:
Error是Throwable的子类之一,包含的方法主要来自于继承自Throwable的那些。Error我们开发者接触到的不多,一般都是和虚拟机相关的问题,如:系统崩溃、虚拟机错误、系统资源如内存不足等,如:OutOfMemoryError等。Error靠程序自身是无法解决的,一般JVM会选择终止程序。
Exception也是Throwable的子类之一。相较于Error而言,但是它可是和我们开发者有很大的关系,也经常会遇到他或他的子类,Exception的子类是用来抽象主要都是程序自身出现类问题,我们开发者需要关注和处理这些问题,保证程序业务正常。
Exception拥有大量的子类,但是大致分为两种:
可检查异常:程序经过编译时,编译器会要求对此类异常进行处理(throws/try-catch)。Exception子类中除去RuntimeException以及他的子类的异常都是可检查异常。处理方式也就是:throws/try-catch,下面会细讲。
在出现 可检查异常 和可能出现 运行时异常 时,需要采用异常处理机制。常见的异常处理机制:
使用throws向方法栈上层抛出:
public void test() thros NumberFormatException{ //TODO //抛出一个可检查异常或可能抛出运行时异常 }
之前写的关于异常处理的运行流程( https://blog.csdn.net/zll_zll... )在这里既不赘述了。
上面的内容都是偏向于理论,这里我们讨论下,实际项目中对于异常的处理方式。
而且在JavaWeb应用中,一旦发生异常,正常的代码逻辑就不能执行了,而去执行异常处理:
我们想要的:
为了实现上面的:
那么怎么才能,既能保证实现我们的预期,又能规避上面操作的缺点呢?
那就是做统一异常处理。
实现的方式/思路:
使用Around Advice(环绕通知),在调用原方法的语句上加上try-catch,完成异常的捕获、日志记录等其他操作。
@Around(value = "execution(public * *Controller.*(..))") public BaseResult catchException(ProceedingJoinPoint joinPoint) { try { //统一返回数据类型(前后端为分离时直接跳转页面即可) BaseResult baseResult = new BaseResult(SUCCESS); baseResult.setData(joinPoint.proceed()); return baseResult; } catch (Throwable e) { //捕获异常 String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); //打上LOG logger.error(className + ":" + methodName + ":" + e); return new BaseResult(FAIL); } }
怎么样?够不够惊艳?什么,还嫌太麻烦?
@Slf4j @ControllerAdvice @ResponseBody //或者@RestControllerAdvice public class GlobalExceptionHandler { /** * @valid参数校验异常处理 * @param e * @return */ @ExceptionHandler(MethodArgumentNotValidException.class) public ResultData validationException(MethodArgumentNotValidException e) { BindingResult bindingResult = e.getBindingResult(); if (bindingResult.hasErrors()) { List<ObjectError> allErrors = bindingResult.getAllErrors(); List<String> messages = new ArrayList<>(); allErrors.forEach(p->{ FieldError fieldError = (FieldError) p; log.error("参数格式错误:参数对象:{};字段:{};错误原因:{}", fieldError.getObjectName(), fieldError.getField(), fieldError.getDefaultMessage()); messages.add(fieldError.getDefaultMessage()); }); return ResultData.error(StringUtils.join(messages,",")); } return ResultData.error("参数格式错误"); } /** * 全局异常处理 * @param e * @return */ @ExceptionHandler(Exception.class) public ResultData globalExceptionHandler(Exception e) { //MyBaseException自定义异常 if (e instanceof MyBaseException) { //自定义异常 log.error("自定义异常",e); return ResultData.error(e.getMessage()); } else { //其他异常 log.error("未知异常", e); //给用户有好的提示(不能直接把异常信息传回前端) return ResultData.error("系统开小差了"); } } }
注意 @ExceptionHandler标识的方法的顺序和拦截的顺序一致,如果异常背前面的捕获了,那么后面的就能捕获了,所以具体的异常要放在前面。
上面的代码来自我自己的开源项目(地址: https://gitee.com/zhanglinlu/... )中的com.zll.dms.aop.GlobalExceptionHandler,需要的可以参考下。
更多项目:gitee地址( https://gitee.com/zhanglinlu )