欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java EE項(xiàng)目中的異常處理總結(jié)(一篇不得不看的文章)

 更新時(shí)間:2016年11月05日 09:46:11   作者:云淡風(fēng)清-北京  
什么是異常?運(yùn)行時(shí)發(fā)生的可被捕獲和處理的錯(cuò)誤。這篇文章主要介紹了Java EE項(xiàng)目中的異常處理總結(jié),有需要的可以了解一下。

為什么要在J2EE項(xiàng)目中談異常處理呢?可能許多java初學(xué)者都想說:“異常處理不就是try….catch…finally嗎?這誰都會(huì)?。 ?。筆者在初學(xué)java時(shí)也是這樣認(rèn)為的。如何在一個(gè)多層的j2ee項(xiàng)目中定義相應(yīng)的異常類?在項(xiàng)目中的每一層如何進(jìn)行異常處理?異常何時(shí)被拋出?異常何時(shí)被記錄?異常該怎么記錄?何時(shí)需要把checked Exception轉(zhuǎn)化成unchecked Exception ,何時(shí)需要把unChecked Exception轉(zhuǎn)化成checked Exception?異常是否應(yīng)該呈現(xiàn)到前端頁面?如何設(shè)計(jì)一個(gè)異常框架?本文將就這些問題進(jìn)行探討。

1. JAVA異常處理

在面向過程式的編程語言中,我們可以通過返回值來確定方法是否正常執(zhí)行。比如在一個(gè)c語言編寫的程序中,如果方法正確的執(zhí)行則返回1.錯(cuò)誤則返回0。在vb或delphi開發(fā)的應(yīng)用程序中,出現(xiàn)錯(cuò)誤時(shí),我們就彈出一個(gè)消息框給用戶。

通過方法的返回值我們并不能獲得錯(cuò)誤的詳細(xì)信息。可能因?yàn)榉椒ㄓ刹煌某绦騿T編寫,當(dāng)同一類錯(cuò)誤在不同的方法出現(xiàn)時(shí),返回的結(jié)果和錯(cuò)誤信息并不一致。

所以java語言采取了一個(gè)統(tǒng)一的異常處理機(jī)制。

什么是異常?運(yùn)行時(shí)發(fā)生的可被捕獲和處理的錯(cuò)誤。

在java語言中,Exception是所有異常的父類。任何異常都擴(kuò)展于Exception類。Exception就相當(dāng)于一個(gè)錯(cuò)誤類型。如果要定義一個(gè)新的錯(cuò)誤類型就擴(kuò)展一個(gè)新的Exception子類。采用異常的好處還在于可以精確的定位到導(dǎo)致程序出錯(cuò)的源代碼位置,并獲得詳細(xì)的錯(cuò)誤信息。

Java異常處理通過五個(gè)關(guān)鍵字來實(shí)現(xiàn),try,catch,throw ,throws, finally。具體的異常處理結(jié)構(gòu)由try….catch….finally塊來實(shí)現(xiàn)。try塊存放可能出現(xiàn)異常的java語句,catch用來捕獲發(fā)生的異常,并對異常進(jìn)行處理。Finally塊用來清除程序中未釋放的資源。不管理try塊的代碼如何返回,finally塊都總是被執(zhí)行。

一個(gè)典型的異常處理代碼

public String getPassword(String userId)throws DataAccessException{
String sql = “select password from userinfo where userid='”+userId +”'”;
String password = null;
Connection con = null;
Statement s = null;
ResultSet rs = null;
try{
con = getConnection();//獲得數(shù)據(jù)連接
s = con.createStatement();
rs = s.executeQuery(sql);
while(rs.next()){
password = rs.getString(1);
}
rs.close();
s.close();
}
Catch(SqlException ex){
throw new DataAccessException(ex);
}
finally{
try{
if(con != null){
con.close();
}
}
Catch(SQLException sqlEx){
throw new DataAccessException(“關(guān)閉連接失敗!”,sqlEx);
}
}
return password;
}

可以看出Java的異常處理機(jī)制具有的優(yōu)勢:

給錯(cuò)誤進(jìn)行了統(tǒng)一的分類,通過擴(kuò)展Exception類或其子類來實(shí)現(xiàn)。從而避免了相同的錯(cuò)誤可能在不同的方法中具有不同的錯(cuò)誤信息。在不同的方法中出現(xiàn)相同的錯(cuò)誤時(shí),只需要throw 相同的異常對象即可。

獲得更為詳細(xì)的錯(cuò)誤信息。通過異常類,可以給異常更為詳細(xì),對用戶更為有用的錯(cuò)誤信息。以便于用戶進(jìn)行跟蹤和調(diào)試程序。

把正確的返回結(jié)果與錯(cuò)誤信息分離。降低了程序的復(fù)雜度。調(diào)用者無需要對返回結(jié)果進(jìn)行更多的了解。

強(qiáng)制調(diào)用者進(jìn)行異常處理,提高程序的質(zhì)量。當(dāng)一個(gè)方法聲明需要拋出一個(gè)異常時(shí),那么調(diào)用者必須使用try….catch塊對異常進(jìn)行處理。當(dāng)然調(diào)用者也可以讓異常繼續(xù)往上一層拋出。

2. Checked 異常 還是 unChecked 異常?

Java異常分為兩大類:checked 異常和unChecked 異常。所有繼承java.lang.Exception 的異常都屬于checked異常。所有繼承java.lang.RuntimeException的異常都屬于unChecked異常。

當(dāng)一個(gè)方法去調(diào)用一個(gè)可能拋出checked異常的方法,必須通過try…catch塊對異常進(jìn)行捕獲進(jìn)行處理或者重新拋出。
我們看看Connection接口的createStatement()方法的聲明。

public Statement createStatement() throws SQLException;
SQLException是checked異常。當(dāng)調(diào)用createStatement方法時(shí),java強(qiáng)制調(diào)用者必須對SQLException進(jìn)行捕獲處理。
public String getPassword(String userId){
try{
……
Statement s = con.createStatement();
……
Catch(SQLException sqlEx){
……
}
……
}

或者

public String getPassword(String userId)throws SQLException{
Statement s = con.createStatement();
}

(當(dāng)然,像Connection,Satement這些資源是需要及時(shí)關(guān)閉的,這里僅是為了說明checked 異常必須強(qiáng)制調(diào)用者進(jìn)行捕獲或繼續(xù)拋出)

unChecked異常也稱為運(yùn)行時(shí)異常,通常RuntimeException都表示用戶無法恢復(fù)的異常,如無法獲得數(shù)據(jù)庫連接,不能打開文件等。雖然用戶也可以像處理checked異常一樣捕獲unChecked異常。但是如果調(diào)用者并沒有去捕獲unChecked異常時(shí),編譯器并不會(huì)強(qiáng)制你那么做。

比如一個(gè)把字符轉(zhuǎn)換為整型數(shù)值的代碼如下:

String str = “123”;
int value = Integer.parseInt(str);

parseInt的方法簽名為:

public staticint parseInt(String s)throws NumberFormatException

當(dāng)傳入的參數(shù)不能轉(zhuǎn)換成相應(yīng)的整數(shù)時(shí),將會(huì)拋出NumberFormatException。因?yàn)镹umberFormatException擴(kuò)展于RuntimeException,是unChecked異常。所以調(diào)用parseInt方法時(shí)無需要try…catch

因?yàn)閖ava不強(qiáng)制調(diào)用者對unChecked異常進(jìn)行捕獲或往上拋出。所以程序員總是喜歡拋出unChecked異常?;蛘弋?dāng)需要一個(gè)新的異常類時(shí),總是習(xí)慣的從RuntimeException擴(kuò)展。當(dāng)你去調(diào)用它些方法時(shí),如果沒有相應(yīng)的catch塊,編譯器也總是讓你通過,同時(shí)你也根本無需要去了解這個(gè)方法倒底會(huì)拋出什么異常??雌饋磉@似乎倒是一個(gè)很好的辦法,但是這樣做卻是遠(yuǎn)離了java異常處理的真實(shí)意圖。并且對調(diào)用你這個(gè)類的程序員帶來誤導(dǎo),因?yàn)檎{(diào)用者根本不知道需要在什么情況下處理異常。而checked異??梢悦鞔_的告訴調(diào)用者,調(diào)用這個(gè)類需要處理什么異常。如果調(diào)用者不去處理,編譯器都會(huì)提示并且是無法編譯通過的。當(dāng)然怎么處理是由調(diào)用者自己去決定的。

所以Java推薦人們在應(yīng)用代碼中應(yīng)該使用checked異常。就像我們在上節(jié)提到運(yùn)用異常的好外在于可以強(qiáng)制調(diào)用者必須對將會(huì)產(chǎn)生的異常進(jìn)行處理。包括在《java Tutorial》等java官方文檔中都把checked異常作為標(biāo)準(zhǔn)用法。

使用checked異常,應(yīng)意味著有許多的try…catch在你的代碼中。當(dāng)在編寫和處理越來越多的try…catch塊之后,許多人終于開始懷疑checked異常倒底是否應(yīng)該作為標(biāo)準(zhǔn)用法了。

甚至連大名鼎鼎的《thinking in java》的作者Bruce Eckel也改變了他曾經(jīng)的想法。Bruce Eckel甚至主張把unChecked異常作為標(biāo)準(zhǔn)用法。并發(fā)表文章,以試驗(yàn)checked異常是否應(yīng)該從java中去掉。Bruce Eckel語:“當(dāng)少量代碼時(shí),checked異常無疑是十分優(yōu)雅的構(gòu)思,并有助于避免了許多潛在的錯(cuò)誤。但是經(jīng)驗(yàn)表明,對大量代碼來說結(jié)果正好相反”

關(guān)于checked異常和unChecked異常的詳細(xì)討論可以參考

《java Tutorial》 http://java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html

使用checked異常會(huì)帶來許多的問題。

checked異常導(dǎo)致了太多的try…catch 代碼

可能有很多checked異常對開發(fā)人員來說是無法合理地進(jìn)行處理的,比如SQLException。而開發(fā)人員卻不得不去進(jìn)行try…catch。當(dāng)開發(fā)人員對一個(gè)checked異常無法正確的處理時(shí),通常是簡單的把異常打印出來或者是干脆什么也不干。特別是對于新手來說,過多的checked異常讓他感到無所適從。

try{
……
Statement s = con.createStatement();
……
Catch(SQLException sqlEx){
sqlEx.PrintStackTrace();
}

或者

try{
……
Statement s = con.createStatement();
……
Catch(SQLException sqlEx){
//什么也不干
}

checked異常導(dǎo)致了許多難以理解的代碼產(chǎn)生

當(dāng)開發(fā)人員必須去捕獲一個(gè)自己無法正確處理的checked異常,通常的是重新封裝成一個(gè)新的異常后再拋出。這樣做并沒有為程序帶來任何好處。反而使代碼晚難以理解。

就像我們使用JDBC代碼那樣,需要處理非常多的try…catch.,真正有用的代碼被包含在try…catch之內(nèi)。使得理解這個(gè)方法變理困難起來

checked異常導(dǎo)致異常被不斷的封裝成另一個(gè)類異常后再拋出

public void methodA()throws ExceptionA{
…..
throw new ExceptionA();
}
public void methodB()throws ExceptionB{
try{
methodA();
……
}catch(ExceptionA ex){
throw new ExceptionB(ex);
}
}
Public void methodC()throws ExceptinC{
try{
methodB();
…
}
catch(ExceptionB ex){
throw new ExceptionC(ex);
}
}

我們看到異常就這樣一層層無休止的被封裝和重新拋出。

checked異常導(dǎo)致破壞接口方法

一個(gè)接口上的一個(gè)方法已被多個(gè)類使用,當(dāng)為這個(gè)方法額外添加一個(gè)checked異常時(shí),那么所有調(diào)用此方法的代碼都需要修改。
可見上面這些問題都是因?yàn)檎{(diào)用者無法正確的處理checked異常時(shí)而被迫去捕獲和處理,被迫封裝后再重新拋出。這樣十分不方便,并不能帶來任何好處。在這種情況下通常使用unChecked異常。

chekced異常并不是無一是處,checked異常比傳統(tǒng)編程的錯(cuò)誤返回值要好用得多。通過編譯器來確保正確的處理異常比通過返回值判斷要好得多。

如果一個(gè)異常是致命的,不可恢復(fù)的。或者調(diào)用者去捕獲它沒有任何益處,使用unChecked異常。

如果一個(gè)異常是可以恢復(fù)的,可以被調(diào)用者正確處理的,使用checked異常。

在使用unChecked異常時(shí),必須在在方法聲明中詳細(xì)的說明該方法可能會(huì)拋出的unChekced異常。由調(diào)用者自己去決定是否捕獲unChecked異常

倒底什么時(shí)候使用checked異常,什么時(shí)候使用unChecked異常?并沒有一個(gè)絕對的標(biāo)準(zhǔn)。但是筆者可以給出一些建議

當(dāng)所有調(diào)用者必須處理這個(gè)異常,可以讓調(diào)用者進(jìn)行重試操作;或者該異常相當(dāng)于該方法的第二個(gè)返回值。使用checked異常。
這個(gè)異常僅是少數(shù)比較高級的調(diào)用者才能處理,一般的調(diào)用者不能正確的處理。使用unchecked異常。有能力處理的調(diào)用者可以進(jìn)行高級處理,一般調(diào)用者干脆就不處理。

這個(gè)異常是一個(gè)非常嚴(yán)重的錯(cuò)誤,如數(shù)據(jù)庫連接錯(cuò)誤,文件無法打開等?;蛘哌@些異常是與外部環(huán)境相關(guān)的。不是重試可以解決的。使用unchecked異常。因?yàn)檫@種異常一旦出現(xiàn),調(diào)用者根本無法處理。

如果不能確定時(shí),使用unchecked異常。并詳細(xì)描述可能會(huì)拋出的異常,以讓調(diào)用者決定是否進(jìn)行處理。

3. 設(shè)計(jì)一個(gè)新的異常類

在設(shè)計(jì)一個(gè)新的異常類時(shí),首先看看是否真正的需要這個(gè)異常類。一般情況下盡量不要去設(shè)計(jì)新的異常類,而是盡量使用java中已經(jīng)存在的異常類。


IllegalArgumentException, UnsupportedOperationException 

不管是新的異常是chekced異常還是unChecked異常。我們都必須考慮異常的嵌套問題。

public void methodA()throws ExceptionA{
…..
throw new ExceptionA();
}

方法methodA聲明會(huì)拋出ExceptionA.

public void methodB()throws ExceptionB

methodB聲明會(huì)拋出ExceptionB,當(dāng)在methodB方法中調(diào)用methodA時(shí),ExceptionA是無法處理的,所以ExceptionA應(yīng)該繼續(xù)往上拋出。一個(gè)辦法是把methodB聲明會(huì)拋出ExceptionA.但這樣已經(jīng)改變了MethodB的方法簽名。一旦改變,則所有調(diào)用methodB的方法都要進(jìn)行改變。

另一個(gè)辦法是把ExceptionA封裝成ExceptionB,然后再拋出。如果我們不把ExceptionA封裝在ExceptionB中,就丟失了根異常信息,使得無法跟蹤異常的原始出處。

public void methodB()throws ExceptionB{
try{
methodA();
……
}catch(ExceptionA ex){
throw new ExceptionB(ex);
}
}

如上面的代碼中,ExceptionB嵌套一個(gè)ExceptionA.我們暫且把ExceptionA稱為“起因異?!?因?yàn)镋xceptionA導(dǎo)致了ExceptionB的產(chǎn)生。這樣才不使異常信息丟失。

所以我們在定義一個(gè)新的異常類時(shí),必須提供這樣一個(gè)可以包含嵌套異常的構(gòu)造函數(shù)。并有一個(gè)私有成員來保存這個(gè)“起因異?!?。

public Class ExceptionB extends Exception{
private Throwable cause;
public ExceptionB(String msg, Throwable ex){
super(msg);
this.cause = ex;
}
public ExceptionB(String msg){
super(msg);
}
public ExceptionB(Throwable ex){
this.cause = ex;
}
}

當(dāng)然,我們在調(diào)用printStackTrace方法時(shí),需要把所有的“起因異?!钡男畔⒁餐瑫r(shí)打印出來。所以我們需要覆寫printStackTrace方法來顯示全部的異常棧跟蹤。包括嵌套異常的棧跟蹤。

public void printStackTrace(PrintStrean ps){
if(cause == null){
super.printStackTrace(ps);
}else{
ps.println(this);
cause.printStackTrace(ps);
}
}

一個(gè)完整的支持嵌套的checked異常類源碼如下。我們在這里暫且把它叫做NestedException

public NestedException extends Exception{
private Throwable cause;
public NestedException (String msg){
super(msg);
}
public NestedException(String msg, Throwable ex){
super(msg);
This.cause = ex;
}
public Throwable getCause(){
return (this.cause ==null ?this :this.cause);
}
public getMessage(){
String message = super.getMessage();
Throwable cause = getCause();
if(cause != null){
message = message + “;nested Exception is ” + cause;
}
return message;
}
public void printStackTrace(PrintStream ps){
if(getCause == null){
super.printStackTrace(ps);
}else{
ps.println(this);
getCause().printStackTrace(ps);
}
}
public void printStackTrace(PrintWrite pw){
if(getCause() == null){
super.printStackTrace(pw);
}
else{
pw.println(this);
getCause().printStackTrace(pw);
}
}
public void printStackTrace(){
printStackTrace(System.error);
}
}

同樣要設(shè)計(jì)一個(gè)unChecked異常類也與上面一樣。只是需要繼承RuntimeException。

4. 如何記錄異常

作為一個(gè)大型的應(yīng)用系統(tǒng)都需要用日志文件來記錄系統(tǒng)的運(yùn)行,以便于跟蹤和記錄系統(tǒng)的運(yùn)行情況。系統(tǒng)發(fā)生的異常理所當(dāng)然的需要記錄在日志系統(tǒng)中。

public String getPassword(String userId)throws NoSuchUserException{
UserInfo user = userDao.queryUserById(userId);
If(user == null){
Logger.info(“找不到該用戶信息,userId=”+userId);
throw new NoSuchUserException(“找不到該用戶信息,userId=”+userId);
}
else{
return user.getPassword();
}
}
public void sendUserPassword(String userId)throws Exception {
UserInfo user = null;
try{
user = getPassword(userId);
//……..
sendMail();
//
}catch(NoSuchUserException ex)(
logger.error(“找不到該用戶信息:”+userId+ex);
throw new Exception(ex);
}

我們注意到,一個(gè)錯(cuò)誤被記錄了兩次.在錯(cuò)誤的起源位置我們僅是以info級別進(jìn)行記錄。而在sendUserPassword方法中,我們還把整個(gè)異常信息都記錄了。

筆者曾看到很多項(xiàng)目是這樣記錄異常的,不管三七二一,只有遇到異常就把整個(gè)異常全部記錄下。如果一個(gè)異常被不斷的封裝拋出多次,那么就被記錄了多次。那么異常倒底該在什么地方被記錄?

異常應(yīng)該在最初產(chǎn)生的位置記錄!

如果必須捕獲一個(gè)無法正確處理的異常,僅僅是把它封裝成另外一種異常往上拋出。不必再次把已經(jīng)被記錄過的異常再次記錄。

如果捕獲到一個(gè)異常,但是這個(gè)異常是可以處理的。則無需要記錄異常

public Date getDate(String str){
Date applyDate = null;
SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);
try{
applyDate = format.parse(applyDateStr);
}
catch(ParseException ex){
//乎略,當(dāng)格式錯(cuò)誤時(shí),返回null
}
return applyDate;
}

捕獲到一個(gè)未記錄過的異?;蛲獠肯到y(tǒng)異常時(shí),應(yīng)該記錄異常的詳細(xì)信息

try{
……
String sql=”select * from userinfo”;
Statement s = con.createStatement();
……
Catch(SQLException sqlEx){
Logger.error(“sql執(zhí)行錯(cuò)誤”+sql+sqlEx);
}

究竟在哪里記錄異常信息,及怎么記錄異常信息,可能是見仁見智的問題了。甚至有些系統(tǒng)讓異常類一記錄異常。當(dāng)產(chǎn)生一個(gè)新異常對象時(shí),異常信息就被自動(dòng)記錄。

public class BusinessException extends Exception {
private void logTrace() {
StringBuffer buffer=new StringBuffer();
buffer.append("Business Error in Class: ");
buffer.append(getClassName());
buffer.append(",method: ");
buffer.append(getMethodName());
buffer.append(",messsage: ");
buffer.append(this.getMessage());
logger.error(buffer.toString());
}
public BusinessException(String s) {
super(s);
race();
}

這似乎看起來是十分美妙的,其實(shí)必然導(dǎo)致了異常被重復(fù)記錄。同時(shí)違反了“類的職責(zé)分配原則”,是一種不好的設(shè)計(jì)。記錄異常不屬于異常類的行為,記錄異常應(yīng)該由專門的日志系統(tǒng)去做。并且異常的記錄信息是不斷變化的。我們在記錄異常同應(yīng)該給更豐富些的信息。以利于我們能夠根據(jù)異常信息找到問題的根源,以解決問題。

雖然我們對記錄異常討論了很多,過多的強(qiáng)調(diào)這些反而使開發(fā)人員更為疑惑,一種好的方式是為系統(tǒng)提供一個(gè)異常處理框架。由框架來決定是否記錄異常和怎么記錄異常。而不是由普通程序員去決定。但是了解些還是有益的。

5. J2EE項(xiàng)目中的異常處理

目前,J2ee項(xiàng)目一般都會(huì)從邏輯上分為多層。比較經(jīng)典的分為三層:表示層,業(yè)務(wù)層,集成層(包括數(shù)據(jù)庫訪問和外部系統(tǒng)的訪問)。

J2ee項(xiàng)目有著其復(fù)雜性,J2ee項(xiàng)目的異常處理需要特別注意幾個(gè)問題。

在分布式應(yīng)用時(shí),我們會(huì)遇到許多checked異常。所有RMI調(diào)用(包括EJB遠(yuǎn)程接口調(diào)用)都會(huì)拋出java.rmi.RemoteException;同時(shí)RemoteException是checked異常,當(dāng)我們在業(yè)務(wù)系統(tǒng)中進(jìn)行遠(yuǎn)程調(diào)用時(shí),我們都需要編寫大量的代碼來處理這些checked異常。而一旦發(fā)生RemoteException這些checked異常對系統(tǒng)是非常嚴(yán)重的,幾乎沒有任何進(jìn)行重試的可能。也就是說,當(dāng)出現(xiàn)RemoteException這些可怕的checked異常,我們沒有任何重試的必要性,卻必須要編寫大量的try…catch代碼去處理它。一般我們都是在最底層進(jìn)行RMI調(diào)用,只要有一個(gè)RMI調(diào)用,所有上層的接口都會(huì)要求拋出RemoteException異常。因?yàn)槲覀兲幚鞷emoteException的方式就是把它繼續(xù)往上拋。這樣一來就破壞了我們業(yè)務(wù)接口。RemoteException這些J2EE系統(tǒng)級的異常嚴(yán)重的影響了我們的業(yè)務(wù)接口。我們對系統(tǒng)進(jìn)行分層的目的就是減少系統(tǒng)之間的依賴,每一層的技術(shù)改變不至于影響到其它層。

//
public class UserSoaImplimplements UserSoa{
public UserInfo getUserInfo(String userId)throws RemoteException{
//……
遠(yuǎn)程方法調(diào)用.
//……
}
}
public interface UserManager{
public UserInfo getUserInfo(Stirng userId)throws RemoteException;
}

同樣JDBC訪問都會(huì)拋出SQLException的checked異常。

為了避免系統(tǒng)級的checked異常對業(yè)務(wù)系統(tǒng)的深度侵入,我們可以為業(yè)務(wù)方法定義一個(gè)業(yè)務(wù)系統(tǒng)自己的異常。針對像SQLException,RemoteException這些非常嚴(yán)重的異常,我們可以新定義一個(gè)unChecked的異常,然后把SQLException,RemoteException封裝成unChecked異常后拋出。

如果這個(gè)系統(tǒng)級的異常是要交由上一級調(diào)用者處理的,可以新定義一個(gè)checked的業(yè)務(wù)異常,然后把系統(tǒng)級的異常封存裝成業(yè)務(wù)級的異常后再拋出。

一般地,我們需要定義一個(gè)unChecked異常,讓集成層接口的所有方法都聲明拋出這unChecked異常。

public DataAccessExceptionextends RuntimeException{
……
}
public interface UserDao{
public String getPassword(String userId)throws DataAccessException;
}
public class UserDaoImplimplements UserDAO{
public String getPassword(String userId)throws DataAccessException{
String sql = “select password from userInfo where userId= ‘”+userId+”'”;
try{
…
//JDBC調(diào)用
s.executeQuery(sql);
…
}catch(SQLException ex){
throw new DataAccessException(“數(shù)據(jù)庫查詢失敗”+sql,ex);
}
}
}

定義一個(gè)checked的業(yè)務(wù)異常,讓業(yè)務(wù)層的接口的所有方法都聲明拋出Checked異常.

public class BusinessExceptionextends Exception{
…..
}
public interface UserManager{
public Userinfo copyUserInfo(Userinfo user)throws BusinessException{
Userinfo newUser = null;
try{
newUser = (Userinfo)user.clone();
}catch(CloneNotSupportedException ex){
throw new BusinessException(“不支持clone方法:”+Userinfo.class.getName(),ex);
}
}
}

J2ee表示層應(yīng)該是一個(gè)很薄的層,主要的功能為:獲得頁面請求,把頁面的參數(shù)組裝成POJO對象,調(diào)用相應(yīng)的業(yè)務(wù)方法,然后進(jìn)行頁面轉(zhuǎn)發(fā),把相應(yīng)的業(yè)務(wù)數(shù)據(jù)呈現(xiàn)給頁面。表示層需要注意一個(gè)問題,表示層需要對數(shù)據(jù)的合法性進(jìn)行校驗(yàn),比如某些錄入域不能為空,字符長度校驗(yàn)等。

J2ee從頁面所有傳給后臺(tái)的參數(shù)都是字符型的,如果要求輸入數(shù)值或日期類型的參數(shù)時(shí),必須把字符值轉(zhuǎn)換為相應(yīng)的數(shù)值或日期值。

如果表示層代碼校驗(yàn)參數(shù)不合法時(shí),應(yīng)該返回到原始頁面,讓用戶重新錄入數(shù)據(jù),并提示相關(guān)的錯(cuò)誤信息。
通常把一個(gè)從頁面?zhèn)鱽淼膮?shù)轉(zhuǎn)換為數(shù)值,我們可以看到這樣的代碼

ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)throws Exception{
String ageStr = request.getParameter(“age”);
int age = Integer.parse(ageStr);
…………
String birthDayStr = request.getParameter(“birthDay”);
SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);
Date birthDay = format.parse(birthDayStr);
}

上面的代碼應(yīng)該經(jīng)常見到,但是當(dāng)用戶從頁面錄入一個(gè)不能轉(zhuǎn)換為整型的字符或一個(gè)錯(cuò)誤的日期值。

Integer.parse()方法被拋出一個(gè)NumberFormatException的unChecked異常。但是這個(gè)異常絕對不是一個(gè)致命的異常,一般當(dāng)用戶在頁面的錄入域錄入的值不合法時(shí),我們應(yīng)該提示用戶進(jìn)行重新錄入。但是一旦拋出unchecked異常,就沒有重試的機(jī)會(huì)了。像這樣的代碼造成大量的異常信息顯示到頁面。使我們的系統(tǒng)看起來非常的脆弱。

同樣,SimpleDateFormat.parse()方法也會(huì)拋出ParseException的unChecked異常。

這種情況我們都應(yīng)該捕獲這些unChecked異常,并給提示用戶重新錄入。

ModeAndView handleRequest(HttpServletRequest request,HttpServletResponse response)throws Exception{
String ageStr = request.getParameter(“age”);
String birthDayStr = request.getParameter(“birthDay”);
int age = 0;
Date birthDay = null;
try{
age=Integer.parse(ageStr);
}catch(NumberFormatException ex){
error.reject(“age”,”不是合法的整數(shù)值”);
}
…………
try{
SimpleDateFormat format = new SimpleDateFormat(“MM/dd/yyyy”);
birthDay = format.parse(birthDayStr);
}catch(ParseException ex){
error.reject(“birthDay”,”不是合法的日期,請錄入'MM/dd/yyy'格式的日期”);
}
}

在表示層一定要弄清楚調(diào)用方法的是否會(huì)拋出unChecked異常,什么情況下會(huì)拋出這些異常,并作出正確的處理。

在表示層調(diào)用系統(tǒng)的業(yè)務(wù)方法,一般情況下是無需要捕獲異常的。如果調(diào)用的業(yè)務(wù)方法拋出的異常相當(dāng)于第二個(gè)返回值時(shí),在這種情況下是需要捕獲。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • logback之自定義指定日志文件存儲(chǔ)目錄方式

    logback之自定義指定日志文件存儲(chǔ)目錄方式

    這篇文章主要介紹了logback之自定義指定日志文件存儲(chǔ)目錄方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • java?http請求設(shè)置代理Proxy的兩種常見方法

    java?http請求設(shè)置代理Proxy的兩種常見方法

    代理是一種常見的設(shè)計(jì)模式,其目的就是為其他對象提供一個(gè)代理以控制對某個(gè)對象的訪問,這篇文章主要給大家介紹了關(guān)于java?http請求設(shè)置代理Proxy的兩種常見方法,需要的朋友可以參考下
    2023-11-11
  • Spring MVC Interceptor 實(shí)現(xiàn)性能監(jiān)控的功能代碼

    Spring MVC Interceptor 實(shí)現(xiàn)性能監(jiān)控的功能代碼

    本篇文章主要介紹了Spring MVC Interceptor 實(shí)現(xiàn)性能監(jiān)控的功能代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • Spring Boot部署到Tomcat過程中遇到的問題匯總

    Spring Boot部署到Tomcat過程中遇到的問題匯總

    這篇文章主要給大家分享了關(guān)于Spring Boot部署到Tomcat過程中遇到的一些問題,文中將解決的方法介紹非常詳細(xì),對同樣遇到這個(gè)問題的朋友具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-03-03
  • SpringBoot獲取yml和properties配置文件的內(nèi)容

    SpringBoot獲取yml和properties配置文件的內(nèi)容

    這篇文章主要為大家詳細(xì)介紹了SpringBoot獲取yml和properties配置文件的內(nèi)容,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • SpringMVC上傳圖片代碼實(shí)例

    SpringMVC上傳圖片代碼實(shí)例

    這篇文章主要介紹了SpringMVC上傳圖片代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • springcloud中Ribbon和RestTemplate實(shí)現(xiàn)服務(wù)調(diào)用與負(fù)載均衡

    springcloud中Ribbon和RestTemplate實(shí)現(xiàn)服務(wù)調(diào)用與負(fù)載均衡

    這篇文章主要介紹了Ribbon和RestTemplate實(shí)現(xiàn)服務(wù)調(diào)用與負(fù)載均衡,想了解負(fù)載均衡的同學(xué)可以參考下
    2021-04-04
  • java Socket實(shí)現(xiàn)網(wǎng)頁版在線聊天

    java Socket實(shí)現(xiàn)網(wǎng)頁版在線聊天

    這篇文章主要為大家詳細(xì)介紹了java Socket實(shí)現(xiàn)網(wǎng)頁版在線聊天具體代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-05-05
  • Mybatis一對多查詢列表屬性處理示例詳解

    Mybatis一對多查詢列表屬性處理示例詳解

    使用MyBatis進(jìn)行多表聯(lián)查的關(guān)鍵是構(gòu)建數(shù)據(jù)庫中表的字段和java中對象的屬性的映射關(guān)系,下面這篇文章主要給大家介紹了關(guān)于Mybatis一對多查詢列表屬性處理的相關(guān)資料,需要的朋友可以參考下
    2023-05-05
  • Java switch()括號內(nèi)參數(shù)的類型要求詳解

    Java switch()括號內(nèi)參數(shù)的類型要求詳解

    這篇文章主要介紹了Java switch()括號內(nèi)參數(shù)的類型要求,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10

最新評論