欢迎关注公众号:其实是白羊。干货持续更新中......
废话不多说,先来几个基础垫吧垫吧。
顾名思义,异常就是不正常呗,这是一种现象,也是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 )