捕获和处理异常
对于自定义的业务异常, 以warn级别的日志记录当前URL、执行方法等信息后,提取异常中的错误码和消息到合适的API包装体返回给API调用方。
对于无法处理的系统异常,以Error级别的日志记录上下文信息(URL、参数、用户ID)后,转换为“服务器忙,请稍后再试”,同样以API包装体返回给调用方。
捕获异常后可以不抛出,但至少要记录日志
可以使用如下方法来保留异常的原始信息
catch (IOException e) {
log.error("文件读取错误", e);
throw new RuntimeException("系统忙请稍后再试");
}
或者把原始异常作为转换后新异常的cause,原始异常信息同样不会丢
catch (IOException e) {
throw new RuntimeException("系统忙请稍后再试", e);
}
- 抛出异常时要指定具体信息
下面的代码中未在()中填写具体的message,导致日志记录为java.lang.RuntimeException: null,容易误解为空指针异常,实际上是因为异常的message为空
throw new RuntimeException();
下面代码中try中的逻辑出现异常,却会被finally中的异常覆盖。为什么异常会被覆盖?因为一个方法无法出现两个异常。
@GetMapping("wrong")
public void wrong() {
try {
log.info("try");
//异常丢失
throw new RuntimeException("try");
} finally {
log.info("finally");
throw new RuntimeException("finally");
}
}
解决方法:finally代码块自己负责异常捕获和处理
@GetMapping("right")
public void right() {
try {
log.info("try");
throw new RuntimeException("try");
} finally {
log.info("finally");
try {
throw new RuntimeException("finally");
} catch (Exception ex) {
log.error("finally", ex);
}
}
}
或者可以把try中的异常作为主异常抛出,使用addSuppressed方法把finally中的异常附加到主异常上
@GetMapping("right2")
public void right2() throw Exception{
Exception e = null;
try{
log.info("try");
throw new RunTimeException("try");
} catch (Exception ex){
e = ex;
} finally {
log.info("finally");
try {
log.info("finally");
try{
throw new RunTimeException("finally");
} catch (Exception ex){
if (e != null){
e.addSuppressed(ex);
}
else {
e = ex;
}
}
}
}
throw e;
}
运行方法可以得到同时包含主异常和被屏蔽的异常。这个就是try-with-resource语句的方法,对于实现了AutoCloseable接口的资源,建议使用try-with-resource来释放资源,否则在释放资源时出现的异常覆盖主异常的问题。
传统try-finally语句,在try中调用read方法,finally中调用close方法,会造成finally异常覆盖try中的异常
使用try-with-resource模式可以避免上述问题:
@GetMapping("useresourceright")
public void useresourceright() throws Exception {
try (TestResource testResource = new TestResource()){
testResource.read();
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
将外部资源的句柄对象的创建放在try后面的括号中,当这个try-catch代码块执行完毕后,Java会确保外部资源的close方法被调用。try-with-resource只是JDK实现的一个语法糖,当上面代码编译后,会成为之前那种复杂的写法。反编译后的代码中有一个addSuppressed方法,叫做异常抑制。