我愛(ài)oo,我愛(ài)java

          交流blog QQ:421057986 oofrank@donews

          Spring 中的異常悖論

          這個(gè)題目可能有些危言悚聽(tīng),使用Spring的同行不用害怕,只是因?yàn)楣ぷ髦信龅揭恍﹩?wèn)題,才偶然想到這個(gè)問(wèn)題。

          首先要明確兩個(gè)問(wèn)題:
          1、系統(tǒng)隔離原則:
              即系統(tǒng)間依賴應(yīng)該是清晰的,不因?yàn)橐粋€(gè)系統(tǒng)的故障影響其他系統(tǒng),甚至整個(gè)系統(tǒng)。
          2、簡(jiǎn)單應(yīng)用習(xí)慣:
              普通程序員只會(huì)處理checked Exception,負(fù)責(zé)任的程序員會(huì)處理被調(diào)用的函數(shù)可能拋出的異常(根據(jù)源碼或javadoc)
           

          我們碰到一個(gè)情況:
          使用Spring的 init 特性初試化一個(gè)bean--一個(gè)使用Qutarz的調(diào)度程序。初試化過(guò)程中會(huì)拋出RuntimeException,從而造成Spring容器的整個(gè)初試化失敗。首先我們修改了程序,捕獲了所有異常,隨后在編碼指南中加入了一句話:"所有使用Spring-init機(jī)制初試化的類必須在init中捕獲所有異常:Exception"。因?yàn)橹挥腥绱瞬拍鼙WC整個(gè)系統(tǒng)不會(huì)因?yàn)榫植繂?wèn)題而完全癱瘓。

          我覺(jué)得這是Spring對(duì)異常處理的一個(gè)悖論:
          正方:底層異常都封裝成Runtime的,經(jīng)過(guò)幾次包裝--->簡(jiǎn)單應(yīng)用習(xí)慣--->異常被拋出領(lǐng)域?qū)?init場(chǎng)景下:由Spring處理)
          反方: 一個(gè)類的失敗可以造成整個(gè)系統(tǒng)的失敗---->Spring被RuntimeException宕掉---->不符合系統(tǒng)隔離原則


          當(dāng)然充分的測(cè)試可能可以解決這個(gè)問(wèn)題:但是要注意,測(cè)試只能證明系統(tǒng)有bug,不能證明系統(tǒng)沒(méi)有bug。

          解決方案:
          1、好的設(shè)計(jì)習(xí)慣:將應(yīng)該隔離的系統(tǒng)隔離開(kāi)-->使用不同的Spring配置文件.
          2、接口層錯(cuò)誤處理:在接口層應(yīng)該盡量對(duì)可以處理的異常進(jìn)行處理,然后以合理的方式傳遞給上層.


          PS:在與人交互的系統(tǒng)中都應(yīng)該給最終用戶合理的錯(cuò)誤提示,所以表現(xiàn)層應(yīng)該盡量捕獲非業(yè)務(wù)的RuntimeException給最終用戶更好的操作感受。

          posted on 2006-01-19 21:45 兼聽(tīng)則明 閱讀(1465) 評(píng)論(19)  編輯  收藏 所屬分類: java

          評(píng)論

          # re: Spring 中的異常悖論 2006-01-20 08:54 darkbluefeeling

          我覺(jué)得初始化如果都出問(wèn)題了,系統(tǒng)就應(yīng)該halt了,把問(wèn)題隱藏起來(lái)可比暴露出來(lái)問(wèn)題多的多。畢竟是初始化,這兒不應(yīng)該出問(wèn)題的。  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 08:59 王麻子

          不是表現(xiàn)層應(yīng)該盡量捕獲非業(yè)務(wù)的RuntimeException給最終用戶更好的操作感受,,
          而是業(yè)務(wù)邏輯層根據(jù)業(yè)務(wù)需求拋出一系列的應(yīng)用異常.
          如果表現(xiàn)層遇到了非應(yīng)用異常的其它情況,就是編程錯(cuò)誤,或系統(tǒng)錯(cuò)誤.  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 10:05 qiu

          測(cè)試只能證明系統(tǒng)有bug,不能證明系統(tǒng)沒(méi)有bug。


          不是應(yīng)該感謝spring幫你找到了一個(gè)bug么?

            回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 10:25 兼聽(tīng)則明

          to: darkbluefeeling
          當(dāng)然,如果重要的系統(tǒng)部分不能init成功,系統(tǒng)應(yīng)該首先排錯(cuò);但如果只是很小的一個(gè)部分不能init成功--就造成系統(tǒng)不能啟動(dòng),我認(rèn)為是系統(tǒng)設(shè)計(jì)有問(wèn)題。
          比如:如果聲卡驅(qū)動(dòng)裝載不成功,操作系統(tǒng)就不能啟動(dòng),只是 "蘭屏",你將大罵操作系統(tǒng),而如果硬盤有故障,操作系統(tǒng)不能引導(dǎo),則是罵不到操作系統(tǒng)。  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 10:34 兼聽(tīng)則明

          to: 王麻子
          我覺(jué)得你說(shuō)的是對(duì)的,但是有時(shí)候需求并沒(méi)有分析完全,在系統(tǒng)上線后才發(fā)生這個(gè)問(wèn)題,即本該是“業(yè)務(wù)需求拋出一系列的應(yīng)用異常.”但實(shí)際拋出了RuntimeException,我認(rèn)為同樣不該展現(xiàn)給客戶“異樣”的異常:
          例:----僅僅是例子!
          我使用Qutarz作調(diào)度,當(dāng)一個(gè)調(diào)度不能被fire時(shí),它將拋出RuntimeException,我們的測(cè)試(案例)沒(méi)有發(fā)現(xiàn)這個(gè)情況,到系統(tǒng)上線時(shí),拋出異常,如果隨便給客戶一個(gè)"系統(tǒng)錯(cuò)誤",我認(rèn)為是很不好的,因?yàn)榭蛻魰?huì)認(rèn)為發(fā)生了很嚴(yán)重的錯(cuò)誤,實(shí)際只是簡(jiǎn)單的輸入錯(cuò)誤而已,即按年調(diào)度,但在開(kāi)始時(shí)間到結(jié)束時(shí)間端內(nèi)沒(méi)有該調(diào)度觸發(fā)的時(shí)機(jī)。
            回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 10:39 兼聽(tīng)則明

          to qiu:
          然,Spring幫我捉了一只蟲(chóng),
          你是否感謝MS幫你捉了 其他軟件的蟲(chóng)呢 --------寫了半天帖子---系統(tǒng)崩潰,沒(méi)能保存----
          在沒(méi)有上線時(shí),觸發(fā)bug非常好!可是如果系統(tǒng)正式運(yùn)行時(shí)發(fā)生的癱瘓級(jí)異常,作為軟件開(kāi)發(fā)者還會(huì)笑的出來(lái)嗎?
          ---顯然,使用某種通知機(jī)制通知管理員,由管理員來(lái)需求開(kāi)發(fā)人員支持,好過(guò)使用系統(tǒng)宕掉的方式通知軟件開(kāi)發(fā)者....  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 10:40 yuxie

          Road從來(lái)沒(méi)有說(shuō)過(guò)底層異常都封裝成Runtime的,他的意思是不能被恢復(fù)的異常你要做成Runtime的(one-on-one那本書),像這個(gè)task的初始化,你捕捉了有什么用呢?異常還是已經(jīng)發(fā)生了,而且沒(méi)有被恢復(fù)!  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 11:20 兼聽(tīng)則明

          to: yuxie

          實(shí)際我都捕獲了,并忽略了----忽略也是一種“你說(shuō)的恢復(fù)”,另外“不能被恢復(fù)的異常你要做成Runtime的”的問(wèn)題在于現(xiàn)在Spring中把所有的異常都封裝成Runtime的了----當(dāng)然,我們還可以通過(guò)捕獲處理我們想處理的問(wèn)題。  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 11:20 pesome

          不能恢復(fù)就一定要做成runtime exception?without ejb還說(shuō)checked exception可以替代返回值呢。要具體問(wèn)題具體分析,不能太教條。  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 11:23 兼聽(tīng)則明

          在充分設(shè)計(jì)和充分測(cè)試的情況下,這個(gè)問(wèn)題可以被控制到很小的范圍,但是誰(shuí)能保證設(shè)計(jì)和測(cè)試足夠"充分"呢?  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 11:44 兼聽(tīng)則明

          checked exception可以替代返回值::確實(shí)如此,實(shí)際上業(yè)務(wù)異常是以返回值的身份出現(xiàn)的,更不該是"異常",比如login()方法可能拋出 UnknownUserException,WrongPasswordException....
          而 addUser() 可以拋出UserExistException 比 主鍵沖突的 DataAccessException好的多.  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 12:50 yuxie

          關(guān)于返回值得問(wèn)題,我認(rèn)為拋出Checked Exception作為返回值是業(yè)務(wù)層的事情,而我們說(shuō)的底層的應(yīng)用,是需要跟業(yè)務(wù)層松耦合的。如果底層(集成層等)拋出一個(gè)checked exception,你的業(yè)務(wù)邏輯層就必須要捕獲它或者throw掉,這就增加了系統(tǒng)的耦合性。所以spring把大部分異常封裝成runtime是有道理的。如果他不做成runtime的,你的業(yè)務(wù)層就必須import org.springframework.…….XXException,緊綁定在spring上,豈不是很郁悶!  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 13:42 兼聽(tīng)則明

          to yuxie:
          你說(shuō)的很對(duì),Spring最好不要侵入我們的領(lǐng)域。

          但對(duì)于 “addUser() 可以拋出UserExistException 比 主鍵沖突的 DataAccessException好的多” 問(wèn)題,我們?cè)鯓硬拍芴幚淼降资菙?shù)據(jù)庫(kù)連接異常,還是僅僅是重復(fù)UserID的輸入錯(cuò)誤(即主鍵沖突--業(yè)務(wù)主鍵)呢?  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 13:50 pesome

          spring設(shè)計(jì)成runtime是合理的,而我們catch它的異常也是合理的  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 15:26 yuxie

          to 兼聽(tīng)則明:
          很簡(jiǎn)單亞,你只要做一個(gè)checkUserExist()的方法就是了,如果返回true就throw exception。通過(guò)catch DataAccessException來(lái)判斷是否用重復(fù)記錄,我覺(jué)得不是一個(gè)好的方法。
          數(shù)據(jù)庫(kù)一旦發(fā)生異常,拋出DataAccessException,就應(yīng)該是一個(gè)非常嚴(yán)重的錯(cuò)誤,此時(shí)系統(tǒng)已無(wú)法正常使用,直接在表現(xiàn)層總的catch一下,給用戶一個(gè)錯(cuò)誤界面就ok了。
          如果在checkUserExist()之后,兩個(gè)用戶同時(shí)插了相同的數(shù)據(jù),這確實(shí)是個(gè)問(wèn)題~歐沒(méi)有想到好的方法  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 15:33 兼聽(tīng)則明

          to yuxie:
          如果數(shù)據(jù)庫(kù)記錄很多,checkUserExist(),成本很高,另外,如果有并發(fā)沖突時(shí)checkUserExist()將很可能失效。--如你所說(shuō)!  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 15:47 pesome

          拋出DataAccessException也不一定是非常嚴(yán)重的問(wèn)題,更不表示系統(tǒng)已無(wú)法正常使用。說(shuō)到底,先check還是拋錯(cuò)都是由成本決定,由出錯(cuò)的幾率決定。  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-20 17:24 兼聽(tīng)則明

          to pesome:
          同意  回復(fù)  更多評(píng)論   

          # re: Spring 中的異常悖論 2006-01-21 17:14 yuxie

          dao層try一下,如果是有重復(fù)記錄,就拋出checked exception(dao層公用的),業(yè)務(wù)層捕獲即可。  回復(fù)  更多評(píng)論   

          主站蜘蛛池模板: 福清市| 会昌县| 罗城| 龙江县| 东方市| 龙游县| 贵阳市| 锦州市| 浮山县| 宜丰县| 湘乡市| 宁德市| 寻乌县| 汉源县| 永泰县| 政和县| 元朗区| 赞皇县| 湟中县| 资中县| 泗洪县| 永靖县| 沈丘县| 无为县| 墨江| 莱西市| 连山| 永修县| 洪雅县| 杭锦旗| 衡南县| 左贡县| 安达市| 胶南市| 长沙县| 郧西县| 遂平县| 连城县| 徐州市| 绥化市| 湘西|