EJB 3规范中概述了两种类型的异常:
应用程序异常
系统异常
应用程序异常是与业务逻辑执行相关的、客户端应该处理的异常。举例来说吧,如果客户端程序传递了一个无效的参数,如错误的信用卡号,就可以抛出一个应用程序异常。
另一方面,系统异常则是由系统级的错误产生,如JNDI(Java Naming and Directory Interface)错误、或无法获取数据库连接。并且,系统异常必须为java.rmi.RemoteException的一个子类,或非应用程序异常的java.lang.RuntimeException的一个子类,从EJB程序的角度来看,我们可编写继承自java.lang.Exception的相应程序异常类,来处理应用程序异常。
而对于系统异常,则应用程序必须捕捉到特定的异常——如由JNDI失败产生的NamingException,再抛出一个EJBException。
客户端视图(Client View)
一个会话Bean可看作是客户端应用程序的一个逻辑扩展,它为程序处理了大量的逻辑与数据。一般来说,一个客户端应用程序可通过会话Bean的客户端视图接口,来访问会话对象,而客户端视图接口就是我们前面讨论过的业务接口。
客户端程序可通过以下三种方式访问会话Bean:
Remote:远程客户程序运行在不同于要访问的会话Bean的另外一个JVM中,如图4所示,其通过Bean的远程业务接口来访问一个会话Bean。一个远程客户程序可以是其他另外的EJB、Java客户端程序、或Java Servlet。同时,远程客户程序具有位置独立性,意味着当它们运行于同一JVM中时,能使用相同的API。
Local:本地客户程序运行于同一JVM中,如图3所示,其通过本地业务接口访问会话Bean。一个本地客户程序可以是其他另外的EJB、使用Java Servlets的Web应用程序、JavaServer Pages(JSP)、JavaServer Faces(JSF)。但本地客户程序具有位置依赖性,表1是远程及本地客户程序的对比。
Web Service:可将无状态会话Bean发布为Web Service,其可被Web Service客户端调用。

表1:
在某些情况中,会话Bean同时需要本地及远程业务接口来支持多种不同类型的客户端程序。而客户端程序可通过依赖性注入或JDNI查询来获取一个会话Bean的业务接口,在调用会话Bean中的方法之前,客户端程序需要通过JNDI来得到Bean的一个桩对象。一旦客户端程序得到桩对象的句柄,它就能调用会话Bean中的所有业务方法。在无状态会话Bean的情况中,每一次调用都可获得一个新的“桩”;而在有状态会话Bean中,“桩”需要在客户端捕捉,这样,容器就知道在随后的调用中,该返回哪一个Bean的实例。如果使用了依赖性注入,则下面这行代码就能得到SearchFacade会话Bean的业务接口:
@EJB SearchFacade searchFacade;
如果客户端程序以远程的方式来访问会话Bean,在通过相关属性获取了上下文接口之后,就可以使用JDNI查询了;本地客户端程序也能使用JNDI查询,但需要编写一点简单的依赖性注入代码。例17是SearchFacadeTest客户端程序的JNDI代码,其查询SearchFacade Bean,调用了wineSearch()业务方法,并输出结果。
要注意的是,如果远程客户端是一个Java应用程序或命令行程序,则可以使用一个应用程序客户端容器来调用会话Bean,它也支持远程客户端的依赖性注入。
例17:SearchFacadeClient.java package com.apress.ejb3.chapter02.client; import com.apress.ejb3.chapter02.SearchFacade; import java.util.List; import javax.naming.InitialContext; import javax.naming.NamingException; public class SearchFacadeTest { public SearchFacadeTest() { } public static void main(String[] args) { SearchFacadeTest searchFacadeTest = new SearchFacadeTest(); searchFacadeTest.doTest(); } @EJB static SearchFacade searchFacade; void doTest(){ InitialContext ic; try { ic = new InitialContext(); System.out.println("SearchFacade查找"); System.out.println("搜索酒类"); List winesList = searchFacade.wineSearch("Red"); System.out.println("正在输出酒类清单"); for (String wine:(List<String>)winesList ){ System.out.println(wine); } } catch (NamingException e) { e.printStackTrace(); } } }
例18是ShoppingCartTest客户端程序,其查询有状态ShoppingCart会话Bean,调用addWineItem()业务方法来向“购物车”中添加一瓶酒,调用getCartItems()业务方法获取购物清单,最终输出购物车中的酒类清单。
例18:ShoppingCartTest.java package com.apress.ejb3.chapter02.client; import com.apress.ejb3.chapter02.ShoppingCart; import java.util.ArrayList; import java.util.List; import javax.naming.InitialContext; import javax.naming.NamingException; public class ShoppingCartTest { public ShoppingCartTest() { } public static void main(String[] args) { ShoppingCartTest shoppingCartTest = new ShoppingCartTest(); shoppingCartTest.doTest(); } @EJB static ShoppingCart shoppingCart; void doTest(){ InitialContext ic; try { ic = new InitialContext(); System.out.println("查找购物车"); System.out.println("正在添加酒类"); shoppingCart.addWineItem("Zinfandel"); System.out.println("正在输出购物车中商品"); ArrayList cartItems = shoppingCart.getCartItems(); for (String wine:(List<String>)cartItems ){ System.out.println(wine); } } catch (NamingException e) { e.printStackTrace(); } } }