① java方法問題
異常的類別 異常的分類有不同方式。這里,我們將討論從 EJB 的角度如何對異常進行分類。EJB 規范將異常大致分成三類:JVM 異常:這種類型的異常由 JVM 拋出。OutOfMemoryError 就是 JVM 異常的一個常見示例。對 JVM 異常您無能為力。它們表明一種致命的情況。唯一得體的退出辦法是停止應用程序伺服器(可能要增加硬體資源),然後重新啟動系統。應用程序異常:應用程序異常是一種定製異常,由應用程序或第三方的庫拋出。這些本質上是受查異常(checked exception);它們預示了業務邏輯中的某個條件尚未滿足。在這樣的情況下,EJB 方法的調用者可以得體地處理這種局面並採用另一條備用途徑。系統異常:在大多數情況下,系統異常由 JVM 作為 RuntimeException 的子類拋出。例如,NullPointerException 或 ArrayOutOfBoundsException 將因代碼中的錯誤而被拋出。另一種類型的系統異常在系統碰到配置不當的資源(例如,拼寫錯誤的 JNDI 查找(JNDI lookup))時發生。在這種情況下,系統就將拋出一個受查異常。捕獲這些受查系統異常並將它們作為非受查異常(unchecked exception)拋出頗有意義。最重要的規則是,如果您對某個異常無能為力,那麼它就是一個系統異常並且應當作為非受查異常拋出。 受查異常是一個作為 java.lang.Exception 的子類的 Java 類。通過從 java.lang.Exception 派生子類,就強制您在編譯時捕獲這個異常。相反地,非受查異常則是一個作為 java.lang.RuntimeException 的子類的 Java 類。從 java.lang.RuntimeException 派生子類確保了編譯器不會強制您捕獲這個異常。清單 1. 三種常見的異常處理做法100 try {101 OrderHome homeObj = EJBHomeFactory.getInstance().getOrderHome();102 Collection orderCollection = homeObj.findByCustomerId(id);103 iterator orderItter = orderCollection.iterator();104 while (orderIter.hasNext()) {105 Order orderRemote = (OrderRemote) orderIter.getNext();106 OrderValue orderVal = orderRemote.getValue();107 if (orderVal.getDate() < "mm/dd/yyyy") {108 OrderItemHome itemHome =EJBHomeFactory.getInstance().getItemHome();109 Collection itemCol = itemHome.findByOrderId(orderId)110 Iterator itemIter = itemCol.iterator();111 while (itemIter.hasNext()) {112 OrderItem item = (OrderItem) itemIter.getNext();113 item.remove();114 }115 orderRemote.remove();116 }117 }118 } catch (NamingException ne) {119 throw new EJBException("Naming Exception occurred");120 } catch (FinderException fe) {121 fe.printStackTrace();122 throw new EJBException("Finder Exception occurred");123 } catch (RemoteException re) {124 re.printStackTrace();125 //Some code to log the message126 throw new EJBException(re);127 }EJB 異常處理探試法EJB 組件應拋出哪些異常?您應將它們記錄到系統中的什麼地方?這兩個問題盤根錯結、相互聯系,應該一起解決。解決辦法取決於以下因素:您的 EJB 系統設計:在良好的 EJB 設計中,客戶機絕不調用實體 EJB 組件上的方法。多數實體 EJB 方法調用發生在會話 EJB 組件中。如果您的設計遵循這些准則,則您應該用會話 EJB 組件來記錄異常。如果客戶機直接調用了實體 EJB 方法,則您還應該把消息記錄到實體 EJB 組件中。然而,存在一個難題:相同的實體 EJB 方法可能也會被會話 EJB 組件調用。在這種情形下,如何避免重復記錄呢?類似地,當一個會話 EJB 組件調用其它實體 EJB 方法時,您如何避免重復記錄呢?很快我們就將探討一種處理這兩種情況的通用解決方案。(請注意,EJB 1.1 並未從體系結構上阻止客戶機調用實體 EJB 組件上的方法。在 EJB 2.0 中,您可以通過為實體 EJB 組件定義本地介面規定這種限制。處理應用程序異常在這一部分及其後的幾個部分中,我們將更仔細地研究用 EJB 異常處理應用程序異常和系統異常,以及 Web 層設計。作為這個討論的一部分,我們將探討處理從會話和實體 EJB 組件拋出的異常的不同方式。實體 EJB 組件中的應用程序異常清單 2 顯示了實體 EJB 的一個 ejbCreate() 方法。這個方法的調用者傳入一個 OrderItemValue 並請求創建一個 OrderItem 實體。因為 OrderItemValue 沒有名稱,所以拋出了 CreateException。清單 2 顯示了 CreateException 的一個很典型的用法。類似地,如果方法的輸入參數的值不正確,則查找程序方法將拋出 FinderException。然而,如果您在使用容器管理的持久性(CMP),則開發者無法控制查找程序方法,從而 FinderException 永遠不會被 CMP 實現拋出。盡管如此,在 Home 介面的查找程序方法的 throws 子句中聲明 FinderException 還是要更好一些。RemoveException 是另一個應用程序異常,它在實體被刪除時被拋出。從實體 EJB 組件拋出的應用程序異常基本上限定為這三種類型(CreateException、FinderException 和 RemoveException)及它們的子類。多數應用程序異常都來源於會話 EJB 組件,因為那裡是作出智能決策的地方。實體 EJB 組件一般是啞類,它們的唯一職責就是創建和取回數據。會話 EJB 組件中的應用程序異常清單 3 顯示了來自會話 EJB 組件的一個方法。這個方法的調用者設法訂購 n 件某特定類型的某商品。SessionEJB() 方法計算出倉庫中的數量不夠,於是拋出 NotEnoughStockException。NotEnoughStockException 適用於特定於業務的場合;當拋出了這個異常時,調用者會得到採用另一個備用途徑的建議,讓他訂購更少數量的商品。清單 3. 會話 EJB 組件中的樣本容器回調方法public ItemValueObject[] placeOrder(int n, ItemType itemType) throwsNotEnoughStockException {//Check Inventory.Collection orders = ItemHome.findByItemType(itemType);if (orders.size() < n) {throw NotEnoughStockException("Insufficient stock for " + itemType);}}
處理系統異常
系統異常處理是比應用程序異常處理更為復雜的論題。由於會話 EJB 組件和實體 EJB 組件處理系統異常的方式相似,所以,對於本部分的所有示例,我們都將著重於實體 EJB 組件,不過請記住,其中的大部分示例也適用於處理會話 EJB 組件。當引用其它 EJB 遠程介面時,實體 EJB 組件會碰到 RemoteException,而查找其它 EJB 組件時,則會碰到 NamingException,如果使用 bean 管理的持久性(BMP),則會碰到 SQLException。與這些類似的受查系統異常應該被捕獲並作為 EJBException 或它的一個子類拋出。原始的異常應被包裝起來。清單 4 顯示了一種處理系統異常的辦法,這種辦法與處理系統異常的 EJB 容器的行為一致。通過包裝原始的異常並在實體 EJB 組件中將它重新拋出,您就確保了能夠在想記錄它的時候訪問該異常。清單 4. 處理系統異常的一種常見方式try {OrderHome orderHome = EJBHomeFactory.getInstance().getOrderHome();Order order = orderHome.findByPrimaryKey(Integer id);} catch (NamingException ne) {throw new EJBException(ne);} catch (SQLException se) {throw new EJBException(se);} catch (RemoteException re) {throw new EJBException(re);}避免重復記錄通常,異常記錄發生在會話 EJB 組件中。但如果直接從 EJB 層外部訪問實體 EJB 組件,又會怎麼樣呢?要是這樣,您就不得不在實體 EJB 組件中記錄異常並拋出它。這里的問題是,調用者沒辦法知道異常是否已經被記錄,因而很可能再次記錄它,從而導致重復記錄。更重要的是,調用者沒辦法訪問初始記錄時所生成的唯一的標識。任何沒有交叉引用機制的記錄都是毫無用處的。請考慮這種最糟糕的情形:單機 Java 應用程序訪問了實體 EJB 組件中的一個方法 foo()。在一個名為 bar() 的會話 EJB 方法中也訪問了同一個方法。一個 Web 層客戶機調用會話 EJB 組件的方法 bar() 並也記錄了該異常。如果當從 Web 層調用會話 EJB 方法 bar() 時在實體 EJB 方法 foo() 中發生了一個異常,則該異常將被記錄到三個地方:先是在實體 EJB 組件,然後是在會話 EJB 組件,最後是在 Web 層。而且,沒有一個堆棧跟蹤可以被交叉引用!幸運的是,解決這些問題用常規辦法就可以很容易地做到。您所需要的只是一種機制,使調用者能夠:訪問唯一的標識查明異常是否已經被記錄了您可以派生 EJBException 的子類來存儲這樣的信息。清單 5 顯示了 LoggableEJBException 子類:清單 5. LoggableEJBException ? EJBException 的一個子類public class LoggableEJBException extends EJBException {protected boolean isLogged;protected String uniqueID;public LoggableEJBException(Exception exc) {super(exc);isLogged = false;uniqueID = ExceptionIDGenerator.getExceptionID();}....}類 LoggableEJBException 有一個指示符標志(isLogged),用於檢查異常是否已經被記錄了。每當捕獲一個 LoggableEJBException 時,看一下該異常是否已經被記錄了(isLogged == false)。如果 isLogged 為 false,則記錄該異常並把標志設置為 true。ExceptionIDGenerator 類用當前時間和機器的主機名為異常生成唯一的標識。如果您喜歡,也可以用有想像力的演算法來生成這個唯一的標識。如果您在實體 EJB 組件中記錄了異常,則這個異常將不會在別的地方被記錄。如果您沒有記錄就在實體 EJB 組件中拋出了 LoggableEJBException,則這個異常將被記錄到會話 EJB 組件中,但不記錄到 Web 層中。清單 6 顯示了使用這一技術重寫後的清單 4。您還可以繼承 LoggableException 以適合於您的需要(通過給異常指定錯誤代碼等)。清單 6. 使用 LoggableEJBException 的異常處理try {OrderHome orderHome = EJBHomeFactory.getInstance().getOrderHome();Order order = orderHome.findByPrimaryKey(Integer id);} catch (NamingException ne) {throw new LoggableEJBException(ne);} catch (SQLException se) {throw new LoggableEJBException(se);} catch (RemoteException re) {Throwable t = re.detail;if (t != null && t instanceof Exception) {throw new LoggableEJBException((Exception) re.detail);} else {throw new LoggableEJBException(re);}}記錄 RemoteException從清單 6 中,您可以看到 naming 和 SQL 異常在被拋出前被包裝到了 LoggableEJBException 中。但 RemoteException 是以一種稍有不同 ? 而且要稍微花點氣力 ? 的方式處理的。 會話 EJB 組件中的系統異常如果您決定記錄會話 EJB 異常,請使用清單 7 所示的記錄代碼;否則,請拋出異常,如清單 6 所示。您應該注意到,會話 EJB 組件處理異常可有一種與實體 EJB 組件不同的方式:因為大多數 EJB 系統都只能從 Web 層訪問,而且會話 EJB 可以作為 EJB 層的虛包,所以,把會話 EJB 異常的記錄推遲到 Web 層實際上是有可能做到的。它之所以不同,是因為在 RemoteException 中,實際的異常將被存儲到一個稱為 detail(它是 Throwable 類型的)的公共屬性中。在大多數情況下,這個公共屬性保存有一個異常。如果您調用 RemoteException 的 printStackTrace,則除列印 detail 的堆棧跟蹤之外,它還會列印異常本身的堆棧跟蹤。您不需要像這樣的 RemoteException 的堆棧跟蹤。為了把您的應用程序代碼從錯綜復雜的代碼(例如 RemoteException 的代碼)中分離出來,這些行被重新構造成一個稱為 ExceptionLogUtil 的類。有了這個類,您所要做的只是每當需要創建 LoggableEJBException 時調用 ExceptionLogUtil.createLoggableEJBException(e)。請注意,在清單 6 中,實體 EJB 組件並沒有記錄異常;不過,即便您決定在實體 EJB 組件中記錄異常,這個解決方案仍然行得通。清單 7 顯示了實體 EJB 組件中的異常記錄:清單 7. 實體 EJB 組件中的異常記錄try {OrderHome orderHome = EJBHomeFactory.getInstance().getOrderHome();Order order = orderHome.findByPrimaryKey(Integer id);} catch (RemoteException re) {LoggableEJBException le =ExceptionLogUtil.createLoggableEJBException(re);String traceStr = StackTraceUtil.getStackTrace(le);Category.getInstance(getClass().getName()).error(le.getUniqueID() +":" + traceStr);le.setLogged(true);throw le;}您在清單 7 中看到的是一個非常簡單明了的異常記錄機制。一旦捕獲受查系統異常就創建一個新的 LoggableEJBException。接著,使用類 StackTraceUtil 獲取 LoggableEJBException 的堆棧跟蹤,把它作為一個字元串。然後,使用 Log4J category 把該字元串作為一個錯誤加以記錄。
StackTraceUtil 類的工作原理
在清單 7 中,您看到了一個新的稱為 StackTraceUtil 的類。因為 Log4J 只能記錄 String 消息,所以這個類負責解決把堆棧跟蹤轉換成 String 的問題。清單 8 說明了 StackTraceUtil 類的工作原理:清單 8. StackTraceUtil 類public class StackTraceUtil {public static String getStackTrace(Exception e){StringWriter sw = new StringWriter();PrintWriter pw = new PrintWriter(sw);return sw.toString();}....}java.lang.Throwable 中預設的 printStackTrace() 方法把出錯消息記錄到 System.err。Throwable 還有一個重載的 printStackTrace() 方法,它把出錯消息記錄到 PrintWriter 或 PrintStream。上面的 StackTraceUtil 中的方法把 StringWriter 包裝到 PrintWriter 中。當 PrintWriter 包含有堆棧跟蹤時,它只是調用 StringWriter 的 toString(),以獲取該堆棧跟蹤的 String 表示。Web 層的 EJB 異常處理在 Web 層設計中,把異常記錄機制放到客戶機端往往更容易也更高效。要能做到這一點,Web 層就必須是 EJB 層的唯一客戶機。此外,Web 層必須建立在以下模式或框架之一的基礎上:模式:業務委派(Business Delegate)、FrontController 或攔截過濾器(Intercepting Filter)框架:Struts 或任何包含層次結構的類似於 MVC 框架的框架為什麼異常記錄應該在客戶機端上發生呢?嗯,首先,控制尚未傳到應用程序伺服器之外。所謂的客戶機層在 J2EE 應用程序伺服器本身上運行,它由 JSP 頁、servlet 或它們的助手類組成。其次,在設計良好的 Web 層中的類有一個層次結構(例如:在業務委派(Business Delegate)類、攔截過濾器(Intercepting Filter)類、http 請求處理程序(http request handler)類和 JSP 基類(JSP base class)中,或者在 Struts Action 類中),或者 FrontController servlet 形式的單點調用。這些層次結構的基類或者 Controller 類中的中央點可能包含有異常記錄代碼。對於基於會話 EJB 記錄的情況,EJB 組件中的每一個方法都必須具有記錄代碼。隨著業務邏輯的增加,會話 EJB 方法的數量也會增加,記錄代碼的數量也會增加。Web 層系統將需要更少的記錄代碼。如果您的 Web 層和 EJB 層在同一地方並且不需要支持任何其它類型的客戶機,那麼您應該考慮這一備用方案。不管怎樣,記錄機制不會改變;您可以使用與前面的部分所描述的相同技術。真實世界的復雜性到現在為止,您已經看到了簡單情形的會話和實體 EJB 組件的異常處理技術。然而,應用程序異常的某些組合可能會更令人費解,並且有多種解釋。清單 9 顯示了一個示例。OrderEJB 的 ejbCreate() 方法試圖獲取 CustomerEJB 的一個遠程引用,這會導致 FinderException。OrderEJB 和 CustomerEJB 都是實體 EJB 組件。您應該如何解釋 ejbCreate() 中的這個 FinderException 呢?是把它當作應用程序異常對待呢(因為 EJB 規范把它定義為標准應用程序異常),還是當作系統異常對待?清單 9. ejbCreate() 方法中的 FinderExceptionpublic Object ejbCreate(OrderValue val) throws CreateException {try {if (value.getItemName() == null) {throw new CreateException("Cannot create Order without a name");}String custId = val.getCustomerId();Customer cust = customerHome.fingByPrimaryKey(custId);this.customer = cust;} catch (FinderException ne) {//How do you handle this Exception ?} catch (RemoteException re) {//This is clearly a System Exceptionthrow ExceptionLogUtil.createLoggableEJBException(re);}return null;} 文字太多打不下了,具體你在看看類似的教程
② 求翻譯解釋一下Java代碼
publicclassTest{
publicstaticStringoutput="";
publicstaticvoidfoo(inti){
try{
if(i==1){
thrownewException();//如果參數為1,拋出異常,進入到catch
}
output+="1";
}catch(Exceptione){
output+="2";//如果參數為1,執行這里
return;
}finally{
output+="3";//不管怎樣這里都要執行
}
output+="4";//這里是最後一個執行語句,拋出異常就不執行這里
}
publicstaticvoidmain(String[]args){
foo(0);//第一次調用
foo(1);//第二次調用
System.out.println(Test.output);
}
}
/*
*現在說下執行步驟:output的值我[]括起來
*第一次調用foo(0):(1)參數為0,所以執行output+="1",那麼output現在為[1];
* (2)執行到output+="3",那麼output現在為[13];
* (3)執行到output+="4";那麼output現在為[134]
*第二次調用foo(1):(1)執行if裡面,拋出異常
* (2)進入到catch,執行output+="2",output現在為[1342]
* (3)進入finally,執行output+="3", output現在為[13423]
*/