前言
某位大佬说过: 当我们开发一个接口的时候有 60 % ~ 70 % 的代码都是在处理错误。
异常
在 Java 中,异常是在程序运行期间发生的不正常情况
异常分类
Java 的异常主要分为两类: 检查性异常 (检查性异常要求我们在编译时必须处理该异常)、非检查性异常 ()非受检异常在运行时可能抛出,无需在编译时强制处理。
- 检查性异常 (Checked Exception): SQLException、IOException、FileNotFoundException、ClassNotFoundException
- 非检查性异常 (Unchecked Exception): IndexOutOfBoundsException、NullPointerException、NumberFormatException
在 Java 中所有的异常都继承与异常基类 Exception 而 Exception 类又继承与顶级错误处理类 Throwable 通过上面的继承关系图我们可以看出无论是 Error 错误还是 Exception 异常都继承与 Throwable。
提示: 当我们需要在项目模块中自定义异常时通常会选择非检查性异常!
public class CommonException extends RuntimeException {
}
异常处理
在 Java 语言中我们处理异常一般会采取两种方式:一种是向上抛异常交给他的调用方进行处理,另一种是通过 try {}catch(Exception ex) {} 来处理异常。
- 第一种
public void testThrowsSolveException() throws IOException{
log.info("测试 throws 异常处理")
}
- 第二种
public void testTryCatchSolveException() {
try {
// 业务方法
} catch(IOException ex) {
}
}
自定义异常
自定义检查性异常
// 自定义检测性异常
public class CustomException extends Exception {
private static final long serialVersionUID = 8312907182931783379L;
private ErrorInfo errorInfo;
private String errorMsg;
private String errorCode;
public CustomException() {
super();
}
public CustomException() {
super();
}
public CustomException(ErrorInfo errorInfo, String errorMsg) {
this.errorMsg = Optional.ofNullable(errorMsg).orElse(errorInfo::getErrorMssg);
this.errorCode = Optional.ofNullable(errorInfo::getErrorCode).orElse(null);
}
}
自定义非检查性异常
public class SystemException extends RuntimeException {
private static final long serialVersionUID = 8312907182931723379L;
private ErrorInfo errorInfo;
private String errorMsg;
private String errorCode;
Project + Moudle =
public SystemException() {
super();
}
public SystemException(String errorMsg) {
super(errorMsg);
}
public CustomException(ErrorInfo errorInfo, String errorMsg) {
this.errorMsg = Optional.ofNullable(errorInfo::getErrorMsg).orElse(errorMsg);
this.errorCode = Optional.ofNullable(errorInfo::getErrorCode).orElse(null);
}
}
异常捕获规范
// 如果是 RPC 调用你的 @Transactional(rollback = Throwable.class)
try{
……
} catch (Throwable e){
// log.error() ????
throw new BIZException(e);
} catch (Biz1Exception e){
throw new BIZException(e.getMessage());
} catch (Biz2Exception e){
throw new Exception(e);
} catch (Biz3Exception e){
throw new Exception(……);
} catch (Biz4Exception e){
……
} catch (Exception e){
throw e;
}
- 不要重复包装同样类型的异常参数。
- 需要抛出对应的异常栈信息。
- 不能使用低抽象级别的异常去包装高抽象级别的异常,这样容易丢失自定义信息 如: 异常码、异常信息。
- 异常转移错误,将业务异常直接转义成系统异常。
- 不抛出异常也不记录 Log , 直接吃掉异常。
统一业务异常处理模板
Sl4j
public abstract class ServiceTemplate<REQUEST,RESPONSE> {
// 业务验证 比如说是否成年 金额是否足够 ? 商品是否还有库存
public abstract boolean isSatisfiedBy(REQUEST requestParam);
// 业务模板
protected RESPONSE process(REQUEST requestParam) {
// 业务校验
try {
// 业务校验
isSatisfiedBy(requestParam);
return doProcess(requestParam);
} catch(ServiceException ex) {
log.warm("发生业务异常: {}", ex.getErrorMsg(), ex);
return Results.fail(ex.getErrorCode(),ex.getErrorMsg());
} catch(SystemException ex) {
log.error("发生系统异常: {}", ex.getErrorMsg(), ex);
return Results.fail(ex.getErrorCode(),ex.getErrorMsg());
} catch(RpcException ex) {
log.error("发生 Rpc 业务异常: {}", ex.getErrorMsg(), ex);
return Results.fail(ex.getErrorCode(),ex.getErrorMsg());
} catch(Throwable ex) {
log.error("发生未知异常: {}", ex.getErrorMsg(), ex);
}
}
// 业务执行
public RESPONSE abstract doProcess(REQUEST requestParam);
}
new ServiceTemplate<Integer,Long>(() -> {
// 业务验证 比如说是否成年 金额是否足够 ? 商品是否还有库存
public boolean isSatisfiedBy(Integer requestParam) {
}
protected Long doProcess( Integer requestParam) {
}
}).process();
Spring Boot 全局异常处理器
@Slf4j
@Aspect
@Component
@RestControllAdiver
public class GlobalExceptionAop {
/**
* execution(* com.ayi.service ..*.*(..)):表示 rpc 接口实现类包中的所有方法
*/
@Pointcut("execution(* com.ayi.service ..*.*(..))")
public void pointcut() {}
@Around(value = "pointcut()")
public Object handleException(ProceedingJoinPoint joinPoint) {
try {
//如果对传入对参数有修改,那么需要调用joinPoint.proceed(Object[] args)
//这里没有修改参数,则调用joinPoint.proceed()方法即可
return joinPoint.proceed();
} catch (ServiceException ex) {
// 对于业务异常,应该记录 warn 日志即可,避免无效告警
log.warn("全局捕获业务异常", ex);
return Results.fail(ex.getCode(), ex.getMessage());
} catch (RpcException ex) {
log.error("全局捕获第三方rpc调用异常", ex);
return Results.fail(ex.getCode(), ex.getMessage());
} catch (SystemException ex) {
log.error("全局捕获系统异常", ex);
return Results.fail(ex.getCode(), ex.getMessage());
} catch (Throwable ex) {
log.error("全局捕获未知异常", ex);
return Results.fail(ex.getMessage());
}
}
}