EJB 编程模型
developerWorks Ken Nordby
enterprise bean 的编程模型的三个关键特征是:面向对象、对象的分布式和使用代理对象。由于此编程模型使用 Java 技术,因此它在本质上就是面向对象的。此模型也是分布式的,这是指 bean 在理论上是位置透明的。根据 Enterprise JavaBeans (EJB) 规范,“一般说来,EJB 类和 EJB 容器的实际位置对客户机是透明的。”在客户机想要访问 EJB 组件时使用代理对象。bean 本身对于客户机是不可访问的,对 bean 方法的访问则由 helper 类提供。
接口、委托和代理
当 Java 程序员编写一个 Enterprise JavaBeans 组件时,他们所创建的类必须实现一个 EJB 接口,并且它必须包含一个名为 ejbCreate() 的方法。一个 EJB 接口 -- 例如 SessionBean 接口 -- 指定了一些方法,它们包括以下各项:
ejbActivate()
ejbPassivate()
ejbRemove()
setSessionContext()
ejbActivate() 和 ejbPassivate() 方法通知一个 bean,管理该 bean 的容器组件正在主动和被动之间切换 bean 的状态(这通常是指在内存中还是交换到磁盘)。ejbRemove() 方法使 bean 知道它已被从容器中删除。setSessionContext() 方法使 bean 与一个上下文对象相关联,此上下文对象是为了便于 bean 与其容器进行通信。
ejbCreate() 方法并不是从零做起创建 enterprise bean 的。当客户机想要创建新的 enterprise bean 时,bean 的容器将调用这个 bean 的类的 newInstance() 方法,来实例化新的 bean 对象。然后容器调用 setSessionContext() 方法来建立上下文对象,用于与 bean 进行通信。最后,容器调用新 bean 中的 ejbCreate() 方法。像 ejbCreate()、ejbActivate() 和 ejbPassivate() 这样的方法有时称为对象生存周期方法,以区别于业务逻辑方法。
当开发人员设计一个新的 EJB 组件时,编写组成 enterprise bean 类的代码本身是不够的。EJB 程序员还必须编写两个将由 helper 类使用的 Java 接口。这些强制性接口必须扩展标准的 EJBObject 和 EJBHome 接口,而这两个接口则都是 java.rmi.Remote marker 接口的扩展。扩展标准 EJBObject 接口的接口被称为 enterprise bean 的远程接口,它指定在 bean 自身中定义的业务方法。当应用程序调用 enterprise bean 中的业务方法时,应用程序并不访问 bean 本身。实际上,方法调用被传递给实现 EJBObject 接口扩展的那个对象。这种做法称为委托,它是 EJB 体系结构中的一个设计要点:
“客户机从来不直接访问 enterprise bean 类的实例。客户机总是使用 enterprise bean 的远程接口来访问 enterprise bean 的实例。实现 enterprise bean 的远程接口的类由容器提供。此类所实现的分布式对象称为 EJB 对象。”(Enterprise JavaBeans Specification 1.0)
bean 对 EJBObject 接口的扩展称为其远程接口,而实现远程接口的对象则称为 EJB 对象。
enterprise bean 还必须具有本地接口。此接口是标准 EJBHome 接口的扩展。实现 bean 的本地接口的对象称为本地对象。本地对象包含一个 create() 方法,此方法由应用程序调用,而应用程序则必须创建一个 bean 实例。本地对象中的 create() 方法创建一个新的 EJB 对象。它并不直接创建新的 enterprise bean 实例,因为不允许直接访问 bean。
EJB 对象和本地对象充当 bean 对象的代理,因为它们代表 bean 接收方法调用。EJB 对象主要为 bean 业务方法充当代理;本地对象主要为 bean 生存周期方法充当代理。
为 EJB 组件使用 create() 方法并不一定要实例化新的 bean。容器确定如何最好地满足创建请求,对于某些类型的 bean,它可以重用现有的实例:
“客户机使用会话 bean 本地接口上的 create 和 remove 方法。虽然客户机以为它正在控制着 EJB 实例的生存周期,但是,是容器在处理 create 和 remove 调用,而不一定要创建和删除 EJB 实例。在客户机和...实例之间不存在固定的映射。容器只是将客户机的工作委托给任何一个方法已经就绪的可用实例而已。”(Enterprise JavaBeans Specification 1.0)
创建新的 bean 实例受容器的控制,并可以与客户机发布 create() 方法异步。
当创建一个 EJB 组件时,开发人员负责定义 EJBObject 接口和 EJBHome 接口,但是无需编写实现这些接口的类的代码。EJB 容器软件组件自动创建这些类。
下面的代码段说明客户机应用程序可能怎样使用称为 CartBean 的 enterprise bean 来进行在线购物:
CartHome cartHome = javax.rmi.PortableRemoteObject.narrow(
initialContext.lookup("applications/shopping_cart"), CartHome.class);
Cart cart = cartHome.create();
cart.addItem(item29);
cart.addItem(item67);
cart.addItem(item91);
cart.purchase();
cart.remove();
CartHome 是实现本地接口的类(EJBHome 接口的扩展)。Cart 是实现远程接口的类(EJBObject 接口的扩展)。当客户机调用应用程序方法(如 addItem() 和 purchase())时,它们是在 cart 对象上调用的,此对象接着将这些方法的执行委托给 bean 自身。enterprise bean 的功能是通过其代理 EJB 对象(即 cart)来获得的。如果多台客户机同时访问 cart bean,将会发生什么事情呢?Enterprise bean 开发人员无需编写代码来支持并发访问。并发性由 EJB 容器支持。
下图说明各 EJB 对象之间的关系:
服务器和容器
EJB 体系结构包括 EJB 服务器和 EJB 容器两个概念。EJB 服务器充当一种组件执行系统,正如 EJB 白皮书中所述:
“Enterprise JavaBeans 规范为每个支持完全可移植性的 Java 应用程序服务器定义了一个标准模型。任何厂商都可以使用此模型来实现对 Enterprise JavaBeans 组件的支持。多种系统(如 TP 监视器、CORBA 运行时系统、COM 运行时系统、数据库系统、Web 服务器系统或其它基于服务器的运行时系统)都可以调整到能够支持可移植的 Enterprise JavaBeans 组件。”(Thomas, Enterprise JavaBeans Technology: Server Component Model for the Java Platform)
EJB 服务器为使用 EJB 组件的应用程序提供操作环境,并供应所有必需的服务,来支持 EJB 体系结构。打包 EJB 服务器软件并没有预先规定的方式。一种方法是将它作为一项功能增强包括到应用程序服务器中,这就是在 IBM WebSphere Application Server, Advanced Edition, Version 2.0 中采用的方法。
EJB 组件并不在 EJB 服务器的顶部直接执行。一个称为 EJB 容器的中间软件组件在 EJB 服务器环境中运行,从而又为这些 bean 自身提供操作环境。EJB 容器对 EJB 应用程序是完全透明的,但是在支持 bean 操作方面起着关键性的作用。
为了使 enterprise bean 能充当可重用的软件组件,它们对特定的服务器或平台功能不能有内建的相关性。服务器端功能的几种常见类型已经被从 bean 设计中“分离出去”,而将此功能的责任转移给了容器组件。例如,容器将被用来接管安全性、并发性、事务处理、交换到辅助存储器和其它服务的责任,从而使 bean 免受服务器相关性的制约,并将按业务逻辑来优化,而不是按服务逻辑来优化。
EJB 白皮书这样描述容器的作用:
“EJB 容器管理部署于其中的 enterprise bean。客户机应用程序并不直接与 enterprise bean 进行交互。相反,客户机应用程序通过由容器生成的两个封装接口( EJB Home 接口和 EJB Object 接口)与 enterprise bean 进行交互。当客户机使用封装接口调用各种操作时,容器截获每个方法调用,并插入管理服务。”(Thomas, Enterprise JavaBeans Technology: Server Component Model for the Java Platform)
可以期望 EJB 容器软件一般都会随 EJB 服务器软件一起提供,尽管规范允许分离这些组件。除了提供对运行时服务(如事务处理和安全性)的访问以外,还期望 EJB 容器包括各种必要工具,来支持 enterprise bean 的安装、操作和管理。例如,需要有工具解释 EJB jar 文件的内容,有工具生成数据库访问,来获得容器提供的持久性,有工具监视正在运行的 bean 的行为,以及实现安全性等。
Bean 风格
EJB 组件分为两种主要类别 -- 会话 bean 和实体 bean。根据 bean 处理状态、事务和持久性的方式这些类别还可以进一步细分。会话 bean 通常具有以下属性:
代表单个客户机执行
可以是事务性的
可以更新共享数据库中的数据
生存期相对较短
其生存期通常就是客户机的生存期
任何持久性数据都由 bean 管理
可以依容器的判断予以删除
会在 EJB 服务器失败时被删除
实体 bean 通常具有以下属性:
代表数据库中的数据
是事务性的
允许多个用户共同访问
可以长期存在
持久性数据可以由容器管理
在 EJB 服务器失败后能继续生存
EJB 规范对会话 bean 和实体 bean 的说明如下:
“对于客户机,会话 enterprise bean 是一种非持久性的对象,它实现某些在服务器上运行的业务逻辑。想像一个会话对象的一种方式是:会话对象是运行在服务器上的客户机程序的逻辑扩展。会话对象不在多台客户机之间共享。
“对于客户机,实体 enterprise bean 是一种持久性对象,它代表一个存储在持久性存储器(例如,一个数据库)中的实体的对象视图,或者是一个由现有企业应用程序实现的实体。”(Enterprise JavaBeans Specification 1.0)
用一种粗略的说法,会话 bean 代表这样的操作,它检索或存储数据以满足用户请求;而实体 bean 则代表一种数据集,可以访问这些数据集来满足用户请求。
会话 bean
最简单的一种 Enterprise JavaBeans 组件就是无状态的会话 bean。因为这些 bean 没有可以区分它们的状态,所有的实例都是完全相同的。容器管理无状态会话 bean 的生存周期,其方式是通过创建足够数目的此种 bean 来适应客户机工作负荷,并在不需要它们时将其删除。钝化,即将闲置的 bean 写到磁盘上,不用于无状态的会话。要调用 bean,客户机程序调用本地接口中的 standard create() 方法,尽管此操作不一定导致实例化新的 bean 实例。容器可以选择将客户机请求发送给现有的对象。反之,容器则可以按它的选择创建新的实例,且独立于由客户机发布的 create() 方法。
在 EJB 本地对象上发布的 create() 调用返回一个对 EJB 对象的引用,这个 EJB 对象代表 enterprise bean。一旦客户机有了 EJB 对象引用,它就可以将业务方法发布到 EJB 对象上,容器随之会将这些方法委托给 bean 自身。负责管理会话 bean 的容器组件无需推断会话 bean 是否是无状态的。会话 bean 是无状态的还是有状态的在安装时声明。
如果会话 bean 在方法调用之间保留状态信息,则它是有状态的。通过调用 ejbPassivate() 方法,容器可以依其判断将有状态会话 bean 钝化,或写到辅助存储器中。EJB 规范并不要求容器在钝化 bean 时使用 Java 串行化协议,但是它们必须提供等价的功能。当容器决定将一个非活动的会话 bean 交换回到内存中时,它会取消被动 bean 的串行化,并调用 ejbActivate() 方法。有状态会话 bean 的开发人员负责确保状态数据是可串行化的。在集群的应用程序服务器环境中实现有状态会话 bean 时务必要小心,因为并不是所有的服务器都支持集群的有状态会话 bean 的同步化。
有状态会话 bean 可以是事务性的。通过使用 javax.transaction.UserTransaction 接口中的方法,如 begin()、commit() 和 rollback(),bean 可以控制事务;通过实现 javax.ejb.SessionSynchronization 接口,bean 可以接收有关事务状态的通知。EJB 容器无需推断哪些 bean 需要事务支持;UserTransaction 接口仅可用于那些在安装时被标记为事务性的 bean。
实体 bean
实体 bean 在体系结构上与会话 bean 类似,但它们提供对企业数据的访问,而不是支持用户会话。一个实体 bean 可以支持多个并发用户,而容器则使访问和事务同步化。实体 bean 还具有支持本地对象中的 finder 方法的主键。知道实体 bean 的主键的客户机可以通过调用本地对象上的 findBy PrimaryKey() 方法获得对象引用。与会话 bean 不同,实体 bean 的本地对象除了具有 create 方法外还具有 finder 方法。
持久性是实体 bean 的一个基本属性。EJB 规范允许两种形式的实体持久性:bean 管理的持久性和容器管理的持久性。对于代表关系数据库中的数据的实体 bean,bean 对持久性的管理意味着,对数据库访问的调用是直接编写在企业 bean 的方法中的(使用 JDBC 或 SQLJ)。这种方法是直截了当的,但它降低了可移植性。容器对持久性的管理意味着 bean 不受数据库调用的影响。在安装时告知容器有关 bean 数据所需的持久性,而容器负责生成实现持久性的代码。这种方法允许 bean 的可移植性更高,甚至达到持久性可使用不同数据源的程度。然而,此方法要求容器中要有复杂功能。
当实体 bean 对象与 EJB 对象相关联时,前者处于就绪状态;否则将认为它们处于共享状态。当客户机调用 EJB 对象中的方法时,容器查找关联的实体 bean 的实例(如果存在的话),或者从共享状态中传送出一个实例。处于就绪状态的实体 bean 可以接收到通过委托从客户机传播给它们的业务方法调用。它们还可以在容器请求时执行 ejbLoad() 和 ejbStore() 方法。load 方法和 store 方法旨在维持实体 bean 和基础数据存储之间数据的一致性。
实体 bean 支持多个用户并发地访问数据。EJB 规范声明,维持数据完整性是容器的责任:
“enterprise bean 开发人员在编写业务方法时无需担心来自多个事务的并发访问。enterprise bean 开发人员在编写方法时可以假定,对于被多个事务同时访问的各个实体 bean,将能确保适当的同步化。”(Enterprise JavaBeans Specification 1.0)
容器完成这一任务通常是通过锁定数据库中的数据,并使访问串行化,或通过创建实体 bean 的多个实例,并允许在基础数据存储中使用并发控制,这样来管理访问。
第三部分内容预告
“什么是 Enterprise JavaBeans 组件?”的第三部分将讨论安装 EJB 组件的特殊部署过程。它还将说明 CORBA 是否是 EJB 组件的竞争对手(答案是“否” -- 请参阅 EJB 技术是如何补充 CORBA 的)。最后,您将看到一种基于 EJB 的三层编程模型的使用情况。
参考资料
查阅 Enterprise JavaBeans Specification 1.0。
要了解有关 EJB 体系结构的更多内容,请参阅 Anne Thomas 的 Enterprise JavaBeans Technology: Server Component Model for the Java Platform,Patricia Seybold Group。
要了解有关 Java 技术和 EJB 体系结构的更多内容,请访问 Sun 的网站。
作者简介
Ken Nordby 是 IBM 软件开发实验室的软件工程师,该实验室位于 Research Triangle Park,North Carolina。作为 SWG Product Affinity Services 业务小组的成员,Ken 与这样一些 IBM 人共事,他们为 IBM 的 WebSphere Application Server(即 Enterprise JavaBeans 技术的 IBM 实现)从事开发和咨询工作。可以通过 [email protected] 与 Ken 联系。
发布人:netbull 来自:LinuxAid