Java中异常应用实践
1、问题:
不同层代码之间的异常情况,通过『返回码包装』还是『通过异常捕获』解决?
Java应用中,上层的代码会调用下层的代码:比如Controller层调用Service层,Service层调用Dao层等等。
上层代码调用下层代码会出错,比如UserServer.getUserById()
这个方法,根据ID获取用户的详细信息。
2、解决方式:
错误的情况下,下层代码需要告诉上层代码错误发生了。
此时有两种通知上层调用方:
方法一:通过返回码的包装
方法二:通过抛出异常
两种详细说明如下:
2.1、 方法一:通过返回码的包装
UserServer.getUserById()
返回一个Result<User>类
,
- 函数定义如下:
Result<User> UserServer.getUserById(int id);
- 其中
Result
定义如下:
public class Result<T> {
private RetCodeEnum retCodeEnum;
private T info;
}
- 调用方调用姿势如下:
Result<User> userResult = userServer.getUserById(5);
if (RetCodeEnum.OK != userResult.getRetCodeEnum) {
//给用户相应提示
}
//继续处理业务
2.2、方法二:通过抛出异常
UserServer.getUserById()
抛出业务异常,
- 函数定义如下:
User UserServer.getUserById(int id) throw BusinessException;
- 其中
BusinessException
定义如下:
public class RetCodeEnumException extends Exception {
private RetCodeEnum retCodeEnum;
}
- 调用方调用姿势如下:
User user = userServer.getUserById(5);
//继续处理业务
- 最后来一个全局的
ExceptionHandler()
来处理业务异常:
protected BaseResp handleException(HttpServletRequest req, Exception e) {
BaseResp baseResp;
if (e instanceof BusinessException) {
BusinessException businessException = (BusinessException) e;
baseResp = new BaseResp(new Result(BusinessException));
} else {
baseResp = new BaseResp(new Result(RetCodeEnum.SYS_BUSY));
LOGGER.error(e.getMessage(), e);
}
return baseResp;
}
3、两种方式比较
第二种调用方式,把错误情况的处理统一到一个Handler处理,完爆第一种方式,理由如下:
3.1、大大减少了重复代码量。
比如你的Java应用有10层,最底层的一个函数有一种错误的情况,那上面9层调用这个方法的100个函数都要加上这么一句:
if (RetCodeEnum.OK != userResult.getRetCodeEnum) {
//给用户相应提示
}
3.2、大大提升了安全性 && 健壮性
第一种方式,每次调用都要添加以上几行代码,而一旦忘记,就意味着 安全问题 or 莫名其妙的错误提示!
第二种方式,因为Exception的抛出是强制的,一旦错误情况发生,一定会中断正常的流程。
3.3、简化错误逻辑处理的代码
某天想在所有错误的情况下打印日志,
- 第一种方式,需要在所有调用的地方打印日志,
LOGGER.error("xxx")
可能要写几千次; - 第二种方式,只需要在ExceptionHandler中添加一次
LOGGER.error("xxx")
即可。
3.4、错误信息更加 清晰结构化
如果
底层的一个方法a出错,导致方法 b、c、d 都产生问题
底层的一个方法m出错,导致方法 o、p、q 都产生问题
- 如果用返回码的方式处理
那么日志里看起来是这样的:
a error
b error
c error
d error
m error
o error
p error
q error
很难区分到底出现了几次问题,每次出问题前因后果是什么
- 如果用异常的方式处理
那么日志里看起来是这样的:
A Exception:
Statck trace:
d
c
b
a
Cause by:
a
B Exception:
Stack trace:
q
p
o
m
Cause by:
m
容易可以看出产生了几次问题,问题的前因后果是怎样的。
4、结论
鉴于以上种种好处,Java中错误情况的处理尽量通过 Exception 解决,而非通过返回码的包装解决。