Java Exception 應用情景(一)

來源:互聯網
上載者:User

Java Exception 應用情景1:使用者登入

經常在登入時,遇到“使用者名稱或密碼錯誤”這樣的提示,讓人搞不清楚究竟是使用者名稱記錯了,還是密碼輸錯了。那麼為什麼程式員不將這兩種情況分開提示呢?

   當Control層(如,servlet)直接調用Model層的"boolean login(String username, String password)"方法,login函數在使用者名稱密碼均正確時,返回true,否則返回false。編寫Control層代碼的程式員可以通過判斷返回 的值來判斷登入是否成功。但是,這樣只能知道使用者是否登入成功,但萬一不成功,卻無法獲知登入失敗的原因。

按照傳統的做法,有兩種方式可以進行“使用者名稱不存在”和“密碼錯誤”的分開提示:

1、在 Control層先調用Model層的 "boolean checkUsernameExist(String username)" 方法,判斷使用者名稱是否存在,若不存在則提示“使用者名稱不存在”。若存在則將使用者名稱和密碼一起傳入Model層的login方法進行函數調用,此時如果 login方法仍返回false,說明密碼錯誤。

優勢:不用改動Model層的方法就可以進行兩種提示

弊端:Control層本應負責資料的打包轉寄(從view層接收資料,封裝後轉給Model層;從Model層獲得回應,將資料重新組織後傳給View層顯示),但現在卻混入了邏輯判斷,Control層的職責變得有些含糊不清。

2、改動Model層的login方法,讓它的傳回值可以包含多種資訊(如,“int login(String userName, String password)”,傳回值0代表成功,1代表)。

優勢:不需要Control層事先做額外的判斷,只需要分析login函數的傳回值就可以知道登入是否成功,以及不成功時的原因

弊端:需要Control層的程式員查看文檔來獲知Model層程式員對傳回值的定義,API不夠友好。

現在,讓我們來分析一下login的流程:

  正常商務邏輯中的意外情況(會妨礙業務的正常進行的事件)就屬於的異常情況,如果這個異常情況是可預知的,那麼就應該定義為Checked Exception,比如使用者名稱不存在或密碼錯誤這種情況就屬於可以預見的意外情況

  異常處理的兩項重要內容分別是:異常恢複和異常記錄。

  異 常恢複的策略針對不同的異常原因而各有不同,例如資料庫伺服器串連失敗時,可以自動間歇性重試;使用者輸入資料錯誤時,可以提示使用者輸入正確的資料,等等。 而異常記錄則是相同的,一般是直接調用系統的日誌外掛程式進行記錄,如logback的Logger對象的error方法,等等。

那麼我們可以設計一個父類,稱為AppException,這個父類中完成異常處理的共性事務,如查詢錯誤碼對應的錯誤資訊、記錄錯誤、傳遞(保留)錯誤鏈等。因此,AppException可以定義如下:

View Code

import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 自訂異常的父類,所有異常均整合自此類. *  * @author Elar * @version 2013.2.28 * */public class AppException extends Exception {    private static final long serialVersionUID = 1L;    private String errorCode;    private String errorInfo;        public AppException(String errorCode, Exception e, String className){        this.errorCode = errorCode;        this.errorInfo = ErrorCode.getErrorCodeInfo(errorCode);        this.initCause(e);        log(className);    }        private void log(String className){        Logger logger = LoggerFactory.getLogger(className);        logger.error(errorCode + ": " + errorInfo);    }}

其中,ErrorCode類用來獲得錯誤碼對應的錯誤資訊。

FailAccessToDBException、UserNameUnavailableException、PasswordErrorException均繼承自AppException,以UserNameUnavailableException為例,可以定義為:

View Code

/** * 使用者名稱不可用異常 * @author Elar * */public class UserNameUnavailableException extends AppException {    private static final long serialVersionUID = 1L;        public UserNameUnavailableException(String errorCode, Exception e, String className){        super(errorCode, e, className);    }}

代碼中,之間調用父類的建構函式,完成異常處理的共性事務。

此時,Model層的login方法可以改寫為:

View Code

/**     * 使用者登陸     * @param username 使用者ID     * @param pw 密碼         * @throws FailAccessToDBException      * @throws IllegalUserNameException      * @throws PasswordErrorException      */    public void login(String username, String pw) throws IllegalUserNameException, PasswordErrorException, FailAccessToDBException {        UserBean userBean = null;        try {            userBean = this.attAdminDao.findByAdminID(username);        } catch (FailAccessToDBException e) {            throw e;        }        if (userBean == null) {            //EC01001 景區管理使用者名錯誤            throw new IllegalUserNameException("EC01001",new Exception(), this.getClass().getName());        } else {            if (userBean.getPassword().equals(pw)) {                logger.info("使用者" + username + "登陸");            } else {                throw new PasswordErrorException("EC01002",new Exception(), this.getClass().getName());            }        }    }

這 樣,Controller層的程式員在調用Model層代碼時,IDE會自動提示這個login函數有可能拋出異常,希望程式員可以應對,這樣程式員就不 用擔心調用的代碼發生意外時,自己不知道而導致錯誤了。Checked Exception相當於顯示的提示了調用代碼的人:“我這個代碼中可能會出現的意外情況,請你做好準備”。

在servlet中,就會收到IDE的提示,從而針對不同的意外情況作出相應的處理:

View Code

try {    userManager.login(userName, password);    // 將使用者名稱放入session中        RequestDispatcher requestDispatcher = req.getRequestDispatcher("prepareHomePage.do");    requestDispatcher.forward(req, resp);} catch (IllegalUserNameException e) {    // 使用者名稱錯誤    String notice = "使用者名稱錯誤";    req.setAttribute("notice", notice);    RequestDispatcher requestDispatcher = req.getRequestDispatcher("login.jsp");    requestDispatcher.forward(req, resp);} catch (PasswordErrorException e) {    // 密碼錯誤    String notice = "密碼錯誤";    req.setAttribute("notice", notice);    RequestDispatcher requestDispatcher = req.getRequestDispatcher("login.jsp");    requestDispatcher.forward(req, resp);} catch (FailAccessToDBException e) {    // 無法串連到資料庫伺服器    String notice = "網路錯誤,請您稍後再試";    req.setAttribute("notice", notice);    RequestDispatcher requestDispatcher = req.getRequestDispatcher("login.jsp");    requestDispatcher.forward(req, resp);}

(這段代碼有很多冗餘的地方,不知道各位看官有沒有好的解決方案……)

 

總結:

  java exception是java獨特的一個元素,在我理解中,它用來保證我們將精力放在正常的商務邏輯上,一般的代碼本身應該主要用來實現正常商務邏輯的執 行。在正常商務邏輯執行的過程中,會產生可預知及不可預知的意外情況,例如記憶體泄露屬於不可預知的意外情況,參數輸錯屬於可預知的意外情況。從前我們都將 exception想得太嚴重,覺得是非法參數、數組越界這些很嚴重的情況才會用到exception,屬於“不得以而處理之”的東西。現在,我們是否應 該好好利用這個java中特別的一份子,來協助我們更好的處理商務程序,將正常流程和意外情況分割開來進行處理,以保證正常業務需求與意外事件分離。

   使用異常還有一個好處是,它顯示的聲明了可能發生的意外情況。例如,在讀取一個資料項目是,也許給出的索引是錯誤的,在從前的代碼中,比較細心些的程式員 會留個心眼檢查一下調用的代碼是否會返回null對象。有一些粗心的程式員會忘記這一點,預設對方會返回一個對象,然後就在自己的代碼中直接調用該對象的 方法,從而在調試時,會莫名其妙的收到null 指標異常。如果下層代碼在撰寫時對索引無效這一個意外情況封裝了一個Checked類型的Exception,那 麼上層調用的程式員就可以在IDE的提示下獲知這一可能發生的意外,從而在代碼中避免調用null 指標的方法而導致錯誤。並且,上層程式員還可以視情況的恢複異 常,即使他無能為力,至少還可以再拋給上層,看看有沒有人可以處理這個意外。也算是程式員之間,一種不見面的契約。

  但是使用異常會有一 個問題,即介面的耦合度變高了。原來只需要參數符合,現在還強制要求處理預見的異常,很多程式員也覺得是一種負擔,代碼上看起來也會不簡潔。而且,如果某 一個業務模組會發生的意外很多,而針對每一個意外情況都定義一種異常的話,會導致介面拋出一長串異常,使代碼變得很難看。所以,如何將性質相近的異常抽象 成一個通用的異常也很考驗java程式員的功力。

  此文是拋磚引玉,向大家介紹小女對java異常的一些看法以及在工程中的一些用法。還希望大家可以多提意見,一起來討論看看java異常應該怎麼用比較合適。希望看官們可以不吝賜教,大家共同進步。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.