如何解決業(yè)務(wù)開(kāi)發(fā)中的異常情況
在我們的業(yè)務(wù)開(kāi)發(fā)中昏兆,經(jīng)常會(huì)碰到一些業(yè)務(wù)方面的異常情況,比如購(gòu)買(mǎi)商品發(fā)現(xiàn)余額不足盾鳞,用戶(hù)未被授權(quán)該操作、以及接收到惡意偽造的請(qǐng)求等等瞻离,這些時(shí)候腾仅,都會(huì)導(dǎo)致我們的業(yè)務(wù)操作終止,返回失敗琐脏。
對(duì)上述類(lèi)似的業(yè)務(wù)異常場(chǎng)景,有兩種方案可供選擇:
- 對(duì)于異常場(chǎng)景返回異常狀態(tài)碼缸兔,在上層日裙,對(duì)異常狀態(tài)碼進(jìn)行處理;
- 拋出異常惰蜜,交由上層邏輯處理昂拂。
這兩種方案都是可行的,但是狀態(tài)碼機(jī)制抛猖,會(huì)給我們的系統(tǒng)帶來(lái)耦合格侯,我們需要維護(hù)一個(gè)統(tǒng)一的狀態(tài)碼機(jī)制,而且容易導(dǎo)致controller層和service層之間的耦合财著。
如何拋出联四、并處理異常?
那我們來(lái)看一下拋出異常的解決方案撑教,我們可以定義一個(gè)ServiceException繼承自RuntimeException朝墩,并通過(guò)ServiceException傳入發(fā)生異常的具體原因提示信息,在Controller層進(jìn)行統(tǒng)一捕獲伟姐,不過(guò)這樣寫(xiě)有點(diǎn)難看收苏。
然而,考慮這樣一種情況愤兵,對(duì)于業(yè)務(wù)異常鹿霸,有些需要我們返回用戶(hù)提示信息,比如“余額不足”等秆乳;有些返回提示信息則可能導(dǎo)致安全隱患懦鼠,比如返回“用戶(hù)不存在”,可以讓黑客發(fā)起“撞庫(kù)”攻擊,這事葛闷,我們對(duì)Exception的處理就要分化了憋槐,可能會(huì)派生出ServiceException1,ServiceException2兩個(gè)異常類(lèi)淑趾,另外對(duì)于可能發(fā)生的代碼問(wèn)題(空指針阳仔、越界等bug),我們還要捕獲RuntimeException扣泊,這又造成了Controller層的耦合和摻雜的業(yè)務(wù)邏輯近范。
有沒(méi)用什么辦法可以規(guī)避這一問(wèn)題呢?
可以借助Spring的ControllerAdvice采用AOP機(jī)制可以對(duì)Controller方法的異常進(jìn)行統(tǒng)一攔截延蟹,并可以針對(duì)不同類(lèi)型的Exception進(jìn)行不同的處理评矩。
@ControllerAdvice
public class ExceptionAdvice {
private static Logger logger =
LoggerFactory.getLogger(AlgoController.class);
@ExceptionHandler({ RuntimeException.class })
@ResponseBody
public ResultSet handleRuntimeException(Exception e){
logger.error("RuntimeException", e);
ResultSet result = ResultSet.valueOf(ResultSetCode.SYSTEM_ERROR);
result.setResult("服務(wù)器開(kāi)小差了");
return result;
}
@ExceptionHandler({ ServiceException.class })
@ResponseBody
public ResultSet handleServiceException(ServiceException e){
logger.warn("ServiceException", e);
ResultSet result = ResultSet.valueOf(ResultSetCode.PARAM_ERROR);
result.setResult(e.getReason());
return result;
}
}
使用異常的注意事項(xiàng)
不要通過(guò)異常來(lái)編寫(xiě)業(yè)務(wù)邏輯(使用異常進(jìn)行條件判斷,性能低阱飘,且難維護(hù))
注意日志的維護(hù)斥杜,捕獲異常(包括在拋出其它異常),要留下詳細(xì)合適的Log信息(也可以將之前的異常傳入拋出的異常)
注意在finally中釋放系統(tǒng)資源
注意try-catch的性能問(wèn)題沥匈,比如不要將其放入循環(huán)內(nèi)
不要捕獲unchecked異常(不可恢復(fù)的場(chǎng)景蔗喂、程序錯(cuò)誤)
...
異常的性能問(wèn)題
在對(duì)性能敏感的場(chǎng)景,異常對(duì)性能會(huì)有一定影響高帖。
下面分析下一場(chǎng)對(duì)性能的影響問(wèn)題:
- 拋出異常缰儿,并捕獲異常
- 獲得異常的調(diào)用棧
- 打印異常日志
其中,最耗費(fèi)性能的肯定是打印日志散址,比獲得異常調(diào)用棧要高一個(gè)量級(jí)乖阵,而2是1的數(shù)倍,當(dāng)然拋出異常并捕獲異常的性能損耗要遠(yuǎn)高于分支判斷(高幾個(gè)量級(jí))预麸。
這里猜測(cè)異常對(duì)于性能的影響主要來(lái)源于對(duì)調(diào)用棧的記錄瞪浸。
不過(guò),不能因噎廢食吏祸,一般情況下默终,合理使用異常,不會(huì)對(duì)系統(tǒng)造成性能負(fù)擔(dān)犁罩,反而會(huì)讓代碼更整潔清晰齐蔽,邏輯合理。
參考文獻(xiàn):
- https://mp.weixin.qq.com/s/9KXrpkJ13qwWpsLPzAz33w(優(yōu)雅處理Java異常)
- https://www.cnblogs.com/lq147760524/p/8475531.html(Java異常及日志注意事項(xiàng))
- https://blog.csdn.net/hustspy1990/article/details/78075394(對(duì)java 異常性能問(wèn)題的分析)