异常处理
Java中的异常处理是开发过程中很重要的一个环节,如果异常处理不好可能会出现无法很快找到 BUG、系统中明明有问题却没有任何异常日志等问题。本文中列出一些异常处理规范,供大家参考。
异常分类
在Java中异常分为
Exception
以及
Error
两种类型,而
Exception
又可分为
Checked Exception
和
Unchecked Exception
。
Error
无需处理。发生
Error
一般都是比较严重的问题,代码层次也处理不了。
Unchecked Exception
对于
Unchecked Exception
也是不处理的,即不用捕获,直接让其抛到顶层,最终交给 Spring 的
ControllerAdvice
中的
ExceptionHandler
来处理。
这类异常通常不捕获代码也是不会报错。而这类异常通常继承自
RuntimeException
。或者说只要是继承自
RuntimeException
都是 Unchecked Exception
Checked Exception
Checked Exception 是在 Java 中的一种异常类型,它是一种编译时异常(checked exception)。在 Java 中,编译时异常是指在编译阶段必须进行处理的异常,即在代码中必须显式地处理这些异常,否则编译器会报错。
Checked Exception 通常是由外部因素导致的异常,例如文件不存在、网络连接中断等,这些异常是程序无法控制的,但是程序需要在编译时就做出相应的处理以避免程序在运行时出错。
如果调用的方法中抛出了异常(Checked Exception),一般情况下需要先捕获,然后包装成自定义的业务异常(如:
ServiceException
)并抛出。如下:
try {
File file = new File("122");
file.createNewFile();
} catch (IOException e) {
throw new ServiceException("XXX_0001", "无法创建文件", e);
}
在新抛出的异常中一定要包含原异常栈。而在后续的异常拦截中也需要将异常栈打印输出,否则会出现具体异常无法找到的问题。
throw new ServiceException("XXX_0001", "无法创建文件");
这样写是不允许的。
在后续处理异常时,要打印完整的异常栈信息。
@ExceptionHandler(value = ServiceException.class)
public ResponseBean<String> serviceException(ServiceException serviceException) {
ResponseBean<String> result = new ResponseBean<>();
result.setErrorCode(serviceException.getErrorCode());
result.setMessage(serviceException.getMessage());
result.setStatus(serviceException.getStatus());
log.error(serviceException.getErrorCode() + "-" + serviceException.getMessage(), serviceException);
return result;
}
业务异常
在系统中一般需要定义一个通用的业务异常,通用业务异常应当定义为 Unchecked Exception,即需要继承自
RuntimeException
。一般通用业务异常中需要包含错误代码,如下:
public class ServiceException extends RuntimeException {
/**
* 错误编码
*/
@Getter
private final String errorCode;
public ServiceException(String message) {
super(message);
this.errorCode = CommonError.SERVICE_ERROR.getErrorCode();
}
public ServiceException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public ServiceException(String errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
public ServiceException(ErrorInfo errorInfo) {
super(errorInfo.getErrorMessage());
this.errorCode = errorInfo.getErrorCode();
}
public ServiceException(ErrorInfo errorInfo, Throwable cause) {
super(errorInfo.getErrorMessage(), cause);
this.errorCode = errorInfo.getErrorCode();
}
}
对于系统中的业务逻辑校验,如必填字段校验不通过、数据重复提交等。则应当已抛出异常的方式来处理。如:添加权限时,首先判断权限编码是否已存在,如果存在抛出异常。
private void checkPermissionCode(String permissionCode, String excludeId) {
Integer count = this.sysPermissionMapper.countByPermissionCode(permissionCode, excludeId);
if (count != null && count > 0) {
throw new ServiceException(PermissionError.PERMISSION_CODE_EXIST);
}
}
除使用通用的业务异常外,也可以自定义业务异常,自定义的业务异常需要继承自
ServiceException
。
一些需要单独处理的 Checked Exception
下面总结了一些需要在 Spring 的
ControllerAdvice
中单独处理的 Checked Exception。
org.springframework.web.HttpRequestMethodNotSupportedException
:请求接口的方法与接口暴露的方法不一致时,Spring 会抛出此异常。
org.springframework.web.bind.MethodArgumentNotValidException
:参数校验不通过时抛出此异常,当使用
@NotNull
、
@Valid
等方式进行请求参数验证时,如若验证不通过会抛出此异常,可以从此异常信息中获取具体的错误信息。
org.springframework.web.bind.MissingServletRequestParameterException
:必填参数缺失时会抛出此异常,当
GET
请求中的参数被标记为
@RequestParam(require = true)
时,如果前端调用接口时未传此参数,会抛出此异常。
org.springframework.web.multipart.MaxUploadSizeExceededException
:当上传的文件超过限制时抛出此异常。文件大小的限制通过
spring.servlet.multipart.max-file-size
和
spring.servlet.multipart.max-request-size
配置。
以下是
ExceptionHandler
的配置参考。