J2EE平台包含了一个针对分布组件模型的规范,这个模型叫做企业JavaBeans(EJB)。如果你已经利用Java工作了一段时间,你就可能对EJB及其同事务逻辑之间的关系有一些基本的了解。在本文中,我们会更加深入地看探讨,如何在同其他资源进行交互操作的同时使用EJB来封装事务逻辑。关于如何使用EJB创建事务逻辑组件的一些背景知识,可以参看我 先前的文章 。你可以在这里下载到本文所涉及的 示例代码 。
层的范围
我们对中间件概念的探讨要从包含有一个客户层和三个服务器层的结构开始。这三个服务器层是下面这些:
表示层——负责接收客户请求,把这些请求分发给事务逻辑组件,然后把回应进行格式化,以便传送回客户端。
事务层——负责接受来自表示层的服务请求,访问数据资源以及其他企业系统,再将结果数据返回给表示层。
数据层——数据资源和外部企业系统的访问点。
我们把JavaBeans用作数据对象使用,这样就能在服务器层之间传递数据。图A说明了这些概念和组件。
图A
组件模型和EJB
软件组件有一些很显著的特点:
自主性——软件组件应该能够不依赖于其他组件或者应用程序而独立工作。
动态可发现性——尽管一个软件组件在桌面环境下可能不需要被动态发现,但是在分布系统的环境下,最好具有这个特性。
事务进程——软件组件应该能够逻辑事务的进程。
包含对外公开的接口——软件组件应该公开一个或者多个接口,用于向外提供对这个组件功能的访问。
能够用于创建其他的组件或者应用程序——软件组件的主要任务是让开发人员能够把一个或多个组件同自定义的应用程序或者系统代码结合起来,从而创建其他的组件或者应用程序。
为了说明组件可以如何被用来创建一个更加复杂的实体,我们可以把软件的组件比作汽车的零部件,汽车的每个零部件(组件)都有其专用的作用(进程)。只要零部件的位置准确且电缆连接正确(接口),它就能够被作为整车(其他组件或者应用程序)的一个组成部分。
作为事务逻辑组件的EJB
EJB组件模型具有我们为软件组件所制定的绝大多数特点:
自主性——EJB能够不依赖于其他组件或者应用程序而独立完成其工作。但是EJB必须在EJB容器内执行。
动态可发现性——EJB使用JNDI来定位主接口、事务方法和元数据。
事务进程——EJB组件从定义上讲就是被封装的事务进程。
包含由对外公开的接口——EJB使用Java接口来指定其事务方法。
能够用于创建其他的组件或者应用程序——就和基本的软件组件一样,EJB能够被集中起来完成更加复杂的功能。
EJB组件模型还加入了许多重要的组件模型特性,这些特性让其成为更能够吸引人的中间件,例如事务处理、安全和数据库的连接性。
事务处理点和数据访问点常常形成分布系统事务逻辑组件的表面。这些方面让EJB成为了一个用于中间件事务逻辑组件的好技术。
EJB和POJO
当对事务逻辑的访问跨越物理边界或者跨越JVM边界的时侯,按传统习惯EJB就会被调用;而这是远程过程的调用就会起作用。但是,EJB规范引入了本地接口,所以EJB提供了一个更加简便的方式把同一个JVM里位于同一个物理层的对象和组件公开了事务逻辑。
使用远程或者本地接口访问EJB组件的灵活性使得很多结构成为可能。例如,根据事务的需要,我们可以从表示层远程调用EJB,或者是从本地事务层的其他plain-old Java对象(POJO),如图B所示。
图B
EJB访问的灵活性让你能够运用合成原理利用多个JavaBeans和一个或者多个POJO创建逻辑组件。让我们看一个简单的EJB应用程序吧,这个程序用一个事务方法公开了一个用户帐号。JavaBean这个账号的远程接口看起来会像Listing A里的一样。那么事务逻辑类就会看起来像Listing B里的一样。最后,客户端的代码会像Listing C里的这样。
Listing A
public interface Account extends javax.ejb.EJBObject, java.rmi.Remote { public String getBalance() throws java.rmi.RemoteException; } Our home interface might look something like the following: public interface AccountHome extends javax.ejb.EJBHome { public Account create() throws javax.ejb.CreateException, java.rmi.RemoteException; }
Listing B
public class AccountBean implements javax.ejb.SessionBean { private javax.ejb.SessionContext ctx; public void ejbActivate() {} public void ejbCreate() {} public void ejbRemove() {} public void ejbPassivate() {} public void setSessionContext(javax.ejb.SessionContext ctx) { this.ctx = ctx; } // business method public String getBalance() throws java.rmi.RemoteException { String balance = accessDataTierAccount(); return(balance); } }
Listing C
public class AccountClient { public String getAccountBalance() { try { javax.naming.Context ctx = getInitialContext(); AccountHome home = (AccountHome)ctx.lookup("Bank.AccountHome"); Account account = home.create(); account.getBalance(); } catch(Exception e) { } } }
现在假设你想要把多个账号作为一个全局帐号向表示层公开。假如你已经为每个帐号创建了EJB,那么你就能够使用Listing D里的POJO把每个EJB账号实例化,并把它们用一个逻辑帐号表示出来。
Listing D
public class AccountPOJO { BankAccount bankAccount; StockAccount stockAccount; DeptStoreAccount deptStoreAccount; public String createEJBs() { try { javax.naming.Context ctx = getInitialContext(); BankAccountHome bankHome = (BankAccountHome)ctx.lookup("Bank.AccountHome"); bankAccount = bankHome.create(); StockAccountHome stockHome = (StockAccountHome)ctx.lookup("Stock.AccountHome"); stockAccount = stockHome.create(); DeptStoreAccountHome deptStoreHome = (DeptStoreAccountHome)ctx.lookup("DeptStore.AccountHome"); deptStoreAccount = deptStoreHome.create(); } catch(Exception e) { } } public AccountInfo[] getAggregateAccountInfo() { AccountInfo[] accountInfo = new AccountInfo[3]; accountInfo[0] = new AccountInfo(bankAccount.getInfo()); accountInfo[1] = new AccountInfo(stockAccount.getInfo()); accountInfo[2] = new AccountInfo(deptStoreAccount.getInfo()); return accountInfo; } }
结束语
我们已经看到了如何使用JavaBeans来创建多层事务逻辑,也看到了EJB组件模型如何为分布组件系统添加了许多重要的特性。我们的下一篇文章会探讨如何使用EJB同其他对象进行交互操作。我们还会看到更多用于创建JavaBeans和用于访问其事务方法的示例代码。