当前位置: 首页 > 图灵资讯 > 技术篇> 全面研读 EJB 2.0

全面研读 EJB 2.0

来源:图灵教育
时间:2024-03-03 10:37:34

全面研读 EJB 2.0 EJB 2.0 显著的变化增强了应用程序开发的灵活性和可移植性

Richard Monson-Haefel OpenEJB 首席设计师2000 年 6 月

新的 EJB 2.0 规范不仅仅是一个新的阶段性发行版本,它增加了许多引人注目的变化,包括 CMP 组件模型中的一些变化和新的变化 bean 类型,它们将提高您在开发应用程序时的灵活性和可移植性。请率先了解这项新规范的功能。它的公开草案已于本月发布。

6 月 2 号发布的 Enterprise JavaBeans 2.0 不仅是分阶段发行版,也是本规范的新版本。整个规范有 500 比以前多页 EJB 1.1 规范长了 200 页 (66%)。这一规范中最重要的变化是容器管理的持久性 (CMP) 所做的改变和引入了一个全新的 bean 类型,即 MessageDrivenBean。

EJB 2.0 大量的变化集中在一种新的变化上 CMP 在组件模型的定义中。它与旧模型完全不同 CMP 模型,因为它引入了一个新的成员,即持久性管理器,并引入了一种新的方法来定义容器管理的字段,以及这些字段和其他字段 bean 与从属对象的关系。

MessageDrivenBean (消息 bean)介绍也很重要。消息 bean 体现出 JMS (Java Message Service)与 EJB 为了创造一个全新的相集成 bean 类型,设计用于处理异步 JMS 消息。这种令人兴奋的新型 bean 为 JMS 客户机提供允许部署它们的组件模型 EJB 丰富而强大的容器系统环境。

该规范还发生了许多其他较小的变化。虽然这些其他变化也很重要,但它们主要涉及使规范更加严格,以消除多义性,并使这些组件具有更高的可移植性。本文集中讨论 EJB 2.0 中引入的新 CMP 和消息 bean 组件模型。

我会提供几个具体的例子,所以读者应该很容易跟上和理解。但是,EJB 初学者可能会发现这种材料很难,因为它假设读者已经对了 EJB 有一个基本的了解。相关的 EJB 详情请参考参考资料。

容器管理的耐久性 容器管理的耐久性在 EJB 2.0 根本变化发生在中间。在 EJB 2.0 在运行过程中,持久性管理器自动处理 CMP 实体 bean 的持久性。根据一种新的抽象持久性方案,持久性管理器负责 bean 持久管理器合约,实体 bean 映射到数据库。此外,持久性管理器还负责实现和实施多种搜索方法,这些搜索方法都是基于一种叫做 EJB QL 新的查询语言。

重要的是要注意以下事实,即符合要求 EJB 2.0 标准化产品必须得到支持 EJB 1.1 CMP 模型,可以支持新的 EJB 2.0 模型。虽然这两种模型不兼容,但必须支持,以确保未来的兼容性 EJB 1.1 模型。

抽象持久性方案 为了解抽象持久性计划是如何工作的,为什么它很重要,我会很快回顾你 EJB 1.1 如何处理? CMP 是的,然后再讨论 EJB 2.0 如何定义它?

EJB 1.1 中的 CMP 模型 在 EJB 1.1 中,bean 开发人员负责将军 bean 类的持久性字段声明为 Java 基本类型或可序列化类型。下面的示例显示了一个 Employee 企业级 bean 类,它是按 EJB 1.1 有几个定义 CMP 字段:

// Employee bean 类 public class EmployeeBean implements java.ejb.EntityBean { // 实例字段 EntityContext ejbContext; // 容器管理的字段 public int identity; public String firstName; public String lastName; public double salary; public Address address; public Integer ejbCreate(int id, String fname, String lname){ identity = id; firstName = fname; lastName = lname; return null; } ... } // Address 从属类 public class Address implements Serializable{ public String street; public String city; public String state; public String zip; }

当关系数据库用于持久性时,基本字段,如 identity、firstName、lastName 和 salary,它们很容易持久,因为它们很好地映射成 SQL 类型,如 INTEGER、CHAR 和 DOUBLE。

在 EJB 1.1 中,CMP bean 的 XML 提供部署描述符 cmp-field 用来识别这个元素 bean 类中的持久性字段(容器管理字段)。如下所示,cmp-field 该元素用于区分写入数据库的字段和不写入数据库的字段。例如,ejbContext 该字段不包括在容器管理的字段列表中,因此它不是一个持久的字段。

<ejb-jar> <enterprise-beans> <entity> <ejb-name>EmployeeEJB</ejb-name> ... <persistence-type>Container</persistence-type> ... <cmp-field><field-name>identity</field-name></cmp-field> <cmp-field><field-name>firstName</field-name></cmp-field> <cmp-field><field-name>lastName</field-name></cmp-field> <cmp-field><field-name>salary</field-name></cmp-field> <cmp-field><field-name>address</field-name></cmp-field> ...

容器提供商为将容器提供一种工具 bean 持久字段映射到数据库表中,通常每个字段都映射到数据库表中 bean 对应一个表。但是,可序列化的类型,如 Address,持久化难度大。在 EJB 1.1 在中间,没有标准的方法将可序列对象映射到关系数据库。虽然 Address 类有自己的字段集,但是 XML 部署描述符没有提供将这些字段映射到数据库的机制。在大多数情况下,人们希望将可序列化的对象(例如) Address)作为二进制类型(有时称为二进制类型(有时称为二进制类型) blob 类型)在数据库表中持续存在。

由于实体 bean 数据方案逐渐复杂,因此问题也变得严重。例如,Employee bean 可能有很多相似之处 Address 子对象,如 Benefits 和 JobPosition。这些子对象被称为从属对象,可以在关系数据库中形成跨表的复杂对象图。另外,EJB 1.1 中的 CMP 在很大程度上,它不足以持久和其他 bean 的关系。在 EJB 1.1 中,如果有 bean 准备和另一个一起维持 bean 容器会自动将主关键字或句柄作为链接。与某些其它 bean 这种关系的性质可能是双向的,或者依赖于一些不容易用主关键字或句柄来表达的字段,以保持它们 bean 上述方法已被证明是一种远未完善的机制。

EJB 2.0 的 CMP 模型 在 EJB 2.0 中,CMP 实体 bean 与持久管理器的新合同使您能够在实体中工作 bean 对更复杂、可移植性更强的关系进行定义,包括 bean 与 bean 之间、bean 与从属对象的关系,甚至从属对象与从属对象的关系。

持久性管理器是新加入的 Enterprise JavaBeans 在部署过程中。容器制造商,或专门从事特定数据库的持久性制造商,将能够提供这种持久性管理器。其思路是用于管理 bean 关系机制与容器分离,容器只负责安全、事务和资源的管理。这种职责的分离使得不同的持久管理器能够与不同的容器一起工作。它也使实体 bean 在不同 EJB 在各种持久管理器之间,制造商之间具有更强的可移植性。

假如你用过或学过 Thought Inc. 可自动生产 EJB 1.1 容器生成 BMP(bean 管理的持久性)bean 的产品 CocoBase,您已经熟悉了如何工作持久性管理器工具。CocoBase 根据 bean 从对象到关系的部署者提供的映射信息 BMP bean 生成所有数据库访问逻辑。在 EJB 2.0 持久性管理器可以根据部署描述符,bean 生成抽象持久性方案和部署者完成的工作提供的信息 CMP 与数据库相关的实体映射。然而,持久性管理器并不局限于关系数据库。它也可以是对象数据库和遗留的系统 ERP 系统(如 SAP)开发持久性管理器。

为了将持久管理器从容器中分离出来,必须定义 bean 与持久管理器的合同。该合同在新的抽象持久性方案中表现出来。这个方案是通过部署描述符中的一组新的 XML 元素和 CMP 实体 bean 定义了一组代码习语。在 EJB 2.0 中,CMP bean 该类声明为抽象类,其持久性字段和关系字段采用抽象读写方法进行访问,而这两种方法的方法特征映射为 XML 部署描述符中的特定元素。

在部署该 bean 根据持久性管理器工具,您将使用它 XML 部署描述符和 bean 类,具体实现这种抽象 bean 类及其从属对象类。具体实现将包括数据访问代码,该代码将在运行时进行 bean 在数据库中实际读写状态。在运行过程中,容器使用由持久管理器工具生成的子类,而不使用 bean 提供者定义的抽象类。

bean 类别的继承层次结构

为使讨论更加充实,这里提供一个 CMP 实例,它更具体地说明了抽象持久性方案是如何工作的。

EJB 2.0 一个例子 CMP 实体 在 EJB 2.0 集装箱管理的实体 bean 它被定义为抽象的,它的持久性字段不是 bean 直接定义类别。开发了一种抽象的持久性方案作为替代品,允许它 bean 提供者间接地声明持久字段和持久字段 bean 关系。下面是 Employee bean 一个例子,它使用了一种新的抽象持久性方案。请注意,该 bean 任何持久性字段都没有在类中声明。

public abstract EmployeeBean implements javax.ejb.EntityBean { . // 实例字段 EntityContext ejbContext; // 容器管理的持久字段 public abstract void setIdentity(int identity); public abstract int getIdentity(); public abstract void setFirstName(String firstName); public abstract String getFirstName(); public abstract void setLastName(String lastName); public abstract String getLastName(); // 容器管理的关系字段 public abstract void setContactInfo(ContactInfo info); public abstract ContactInfo getContactInfo(); ... }

在此 bean 的 XML 在部署描述符中,抽象的持久性方案声明了容器管理的各个字段和关系。

<ejb-jar> <enterprise-beans> <entity> <ejb-name>EmployeeEJB</ejb-name> ... <persistence-type>Container</persistence-type> ... <cmp-field><field-name>identity</field-name></cmp-field> <cmp-field><field-name>firstName</field-name></cmp-field> <cmp-field><field-name>lastName</field-name></cmp-field> ... </entity> </enterprise-beans> <dependents> <dependent> <dependent-class>ContactInfo</dependent-class> <dependent-name>ContactInfo</dependent-name> <cmp-field>street</cmp-field> <cmp-field>city</cmp-field> <cmp-field>state</cmp-field> <cmp-field>zip</cmp-field> <cmp-field>homePhone</cmp-field> <cmp-field>workPhone</cmp-field> <cmp-field>email</cmp-field> ... </dependent> <relationships> <ejb-relation> <ejb-relation-name>Employee-ContactInfo</ejb-relation-name> <ejb-relationship-role> <ejb-relationship-role-name> employee-has-contactinfo </ejb-relationship-role-name> <multiplicity>one</multiplicity> <role-source> <ejb-name>EmployeeEJB</ejb-name> </role-source> <cmr-field> <cmr-field-name>contactInfo</cmr-field-name> <cmr-field-type>ContactInfo</cmr-field-type> </cmr-field> </ejb-relationship-role> <ejb-relationship-role> <ejb-relationship-role-name> contactinfo_belongsto_employee </ejb-relationship-role-name> <multiplicity>one</multiplicity> <role-source> <dependent-name>ContactInfo<dependent-name> </role-source> </ejb-relationship-role> </ejb-relation> </relationships> <ejb-jar>

用来描述容器管理的关系 XML 元素可能会变得非常复杂,因为它们必须处理各种关系的对应性和方向(单向或双向)。为了描述上述代码段的描述 bean 你需要哪些元素与从属对象类的简单关系。即使是简单的关系也会转化为冗长 XML,但所有这些元素都是必要的,以便持久性管理器能够将复杂的对象图映射到数据库中。

虽然用于定义 CMP bean 抽象持久性方案 XML 元素是 EJB 2.0 中的 CMP 但为了简洁起见,本文不再提供主要问题 XML 示例。作为替代品,本文将纯粹依靠它 bean 必须在类中使用的抽象习语来解释 EJB 2.0 中的 CMP 背后的基本概念。这些代码习语和 XML 部署描述符中的关系元素,并由后者定义,所以你不能只有一个而没有另一个,但它们比这个方案更好 XML 部分比较容易理解。

除了 XML 除了元素之外,抽象的持久性方案还定义了一组习语,它们在声明中 bean 必然会使用类别及其相关对象。严格定义了用于访问和修改字段的方法,要求使用 set<METHOD> 修改持久性字段的方法 get<METHOD> 访问它们的方法。这些方法的名称和返回类型由部署描述符对应 XML 关系元素的规定。

实体 bean 类和从属类都遵循相同的抽象持久性方案。以下是如何处理 ContactInfo 对象被定义为从属对象类的示例。

public abstract class ContactInfo { // 家庭地址信息 public abstract void setStreet(String street); public abstract String getStreet(); public abstract void setState(String state); public abstract String getState(); public abstract void setZip(String zip); public abstract String getZip(); public abstract void setHomePhone(String phone); public abstract String getHomePhone(); // 工作地址信息 public abstract void setWorkPhone(String phone); public abstract String getWorkPhone(); public abstract void setEMail(String email); public abstract String getEMail(); ... }

从属对象与实体一起 bean 随实体的存在而存在, bean 暂停和暂停是对从属对象和实体的理解 bean 关系的关键。从属对象包含在特定的实体中,因此删除该实体也会导致从属对象被删除。在关系数据库的术语中,有时这被称为级联删除。

从属对象,如 ContactInfo,用于关系字段。与实体。 bean 从属对象技术上称为从属对象类。EJB 客户端应用永远不能直接访问从属对象类;这种类型不能用作 bean 远程或本地接口中的参数或返回值。从属对象类只对 bean 类是可见的。

由于它们与从属对象类不适合作为远程参数类型 bean 运行中的持久性逻辑密切相关。持久管理器扩展了抽象的从属对象类,以便在运行过程中提供管理的实现 bean 持久状态。此外,抽象的持久性方案也是数据建模 -- 而不是为了那些企业级的人而不是为了那些 bean 表示业务概念建模 -- 因此,作为一种设计策略,抽象的持久性方案是对的 EJB 隐藏客户机是有意义的。

例如,ContactInfo 除了关系字段 bean 除了客户机所需的简单地址信息外,还有许多其他信息。尽管您可以在抽象持久性方案中使用从属对象类别 ContactInfo(它对 bean 客户机是隐藏的),但是,您必须使用其他对象向客户机实际显示这些数据。下面是一个解释如何正确的例子 EJB 客户机隐藏 ContactInfo 从属对象。在这种情况下,地址信息是通过的 EJB 1.1 开发的示例 Address 对象来表露。

// Employee bean 的远程接口 public interface Employee extends javax.ejb.EJBObject { public Address getHomeAddress(); public void setHomeAddress(Address address); public int getIdentity() throws RemoteException; public void setFirstName(String firstName) throws RemoteException; public String getFirstName()throws RemoteException; public void setLastName(String lastName) throws RemoteException; public String getLastName() throws RemoteException; } // Employee bean 的 bean 类 public abstract EmployeeBean implements javax.ejb.EntityBean { ... public Address getHomeAddress(){ ContactInfo info = getContactInfo(); Address addr = new Address(); addr.street = info.getStreet(); addr.city = info.getCity(); addr.state = info.getState(); addr.zip = info.getZip(); return addr; } public void setHomeAddress(Address addr){ ContactInfo info = getContactInfo(); info.setStreet(addr.street); info.getCity(addr.city); info.getState(addr.state); info.getZip(addr.zip); } ... // 容器管理的关系字段 public abstract void setContactInfo(ContactInfo info); public abstract ContactInfo getContactInfo(); ... }

虽然容器管理的关系字段没有暴露给客户机,但您仍然可以直接从远程接口使用容器管理的持久字段。请注意,用于访问 firstName 和 lastName 在远程接口中使用了容器管理的持久性字段。

一个 bean 通过这种关系的对应性和方向来定义,可能与各种从属对象类有各种不同的关系。Bean 可以与从属对象类有一对多和一对一的关系。例如,Employee bean 也许只有一个 Benefit 从属对象类,但可能有很多 ContactInfo 从属对象类。

public abstract EmployeeBean implements javax.ejb.EntityBean { ... public abstract void setContactInfos(Collection addresses); public abstract Collection getContactInfos(): public abstract void setBenefit(Benefit benefit); public abstract Benefit getBenefit(); ... }

与从属对象类的一对多关系可以表示为 java.util.Collection 类型也可以表示为 ava.util.Set 类型(注:本规范后续版本中,java.util.Map 和 java.util.List 被视为额外的返回类型),而与从属对象的一对一关系则使用从属对象的类型。

实体 bean 与其他实体也可以定义和定义 bean 的关系。这些关系可以是一对一、一对多或多对多。例如,Employee bean 可能有很多子级 bean,而且只有一对 bean。以下代码段采用抽象持久性方案的方法习语,说明了如何为这些关系建模。在应用程序中,子级 bean 和配对的 bean 都表现为 Person bean。

public abstract EmployeeBean implements javax.ejb.EntityBean { ... public abstract void setSpouse(Person manager); public abstract Person getSpouse(); public abstract void setChildren(Collection family); public abstract Collection getChildren(); ... }

与另一个 bean 一对多关系表示为 java.util.Collection 类型或 java.util.Set 该类型用于一对一关系 bean 远程接口类型。

从属对象本身与同一个 bean 其他从属对象之间可以有一对一、一对多和多对多的关系。此外,从属对象和其他实体 bean(除其父级 bean 此外,还可以有一对一、一对多的关系。下面的示例显示,Benefit 从属对象类和 Salary 如何与从属对象(一种报酬计算程序)有一对一的关系? Investment bean 怎样才能有一对多的关系?

public abstract class Benefit { public abstract void setSalary(Salary salary); public abstract Salary getSalary(); public abstract void setInvestments(Collection investments); public abstract Collection getInvestments(); }

在部署过程中,部署者将使用持久性管理工具来实现这一目标 bean 类别及其从属类别。这些具体实现将在运行过程中保持各种关系,并使各种关系 bean 例子的状态与数据库同步。容器将在运行过程中管理持久的例子,以提供具有自动访问控制和事务控制的强大环境。

bean 也可以定义从属对象的值,这些对象是可序列的对象,如 EJB 1.1 示例中的 Address 对象。这些值通过序列化变得持久,它们不会形成和形成,也不会形成它们。 bean 的关系 -- 它们是严格容器管理的持久字段。

容器和持久管理器之间也定义了一个合同,使持久管理器能够获得事务的句柄,并访问由容器管理的数据库连接池。该合同有点宽松,未来需要更严格,但允许持久管理器跨度 EJB 集装箱移植的基础。集装箱与持久管理器之间合同的细节已超出本文的范围。

除了通过抽象持久性方案定义持久性外,EJB 2.0 它还提供了一种新的查询语言,用来说明持久性管理器应该如何实现? CMP 各种搜索方法。

EJB 查询语言 EJB 查询语言 (EJB QL) 规定了如何在本地界面中定义持久性管理器的各种搜索方法。 EJB QL 以 SQL-92 持久性管理器可以自动编译为基础,使实体 bean 它具有更高的可移植性,更容易部署。

EJB QL 和查找方法 EJB QL 句子在实体 bean 在部署描述符中声明。使用 EJB QL 很简单。举个例子,Employee bean 本地界面可以通过以下方式说明:

public interface EmployeeHome extends javax.ejb.EJBHome { ... public Employee findByPrimaryKey(Integer id) throws RemoteException, CreateException; public Collection findByZipCode(String zipcode) throws RemoteException, CreateException; public Collection findByInvestment(String investmentName) throws RemoteException, CreateException; }

给出上述本地界面定义后,即可使用 EJB QL 指定如何执行持久性管理器的搜索方法。每个实体 bean 一定要有一个 findByPrimaryKey() 方法。执行该方法所需的查询是显而易见的 -- 在数据库中使用主关键字(一个或几个)字段 bean,没有必要这样做 EJB QL 语句。

findByZipCode() 该方法用于获得所有具有邮政编码的邮政编码 Employee bean。使用部署描述符中的以下内容 EJB QL 来表达。

FROM contactInfo WHERE contactInfo.zip = ?1

这句话本质上意味着“选择其邮政编码等于” zipcode 参数的所有 Employee bean”。

用于搜索方法 EJB QL 不需要在句子中使用 SELECT 用句子来表示要选择的内容。这是因为搜索方法总是会选择自己的 bean 相同类型的远程引用。在这种情况下,可以认为选择句子将返回远程 Employee bean 所有引用。

如果各种搜索方法一起部署在同一个地方 ejb-jar 如果文件中有可导航的实际关系,这些搜索方法甚至可以跨越其他方法 bean 去抽象持久性方案。例如,findByInvestment() 方法将要求搜索和查询 Employee 导航到投资 bean 去抽象持久性方案。表达这种搜索操作的声明 EJB QL 语句如下所示。

FROM element IN benefit.investments WHERE element.name = ?1

上述句子是:“选择所有这些句子 Employee bean:其利润从属对象至少包括一项投资 bean 引用,其名称等于 findByInvestment() 方法的 investmentName 参数。”

EJB QL 和选择方法 EJB QL 也用于一个名字 ejbSelect 在新的查询方法中,这种方法类似于搜索方法,只是它仅供使用 bean 使用类别。该方法不在本地界面中声明,因此不向客户机显示。此外,ejbSelect 该方法可以返回范围较大的各种值,而不仅限于 bean 远程接口类型本身。

有两种选择:ejbSelect<METHOD> 和 ejbSelect<METHOD>InEntity。ejbSelect<METHOD> 该方法是全局实施的,这意味着该方法不是专门用于实施该方法的 bean 实例。ejbSelect<METHOD>InEntity 该方法的实体实例是专门用于执行该方法的。这些选择方法是 bean 该类被声明为抽象方法,并在这些业务方法中使用。下面是 ejbSelect<METHOD> 方法和 ejbSelect<METHOD>InEntity 该方法的例子也说明了如何在业务方法中使用它们。

public abstract class EmployeeBean implements javax.ejb.EntityBean { ... // ejbSelectInEntity public abstract Collection ejbSelectInvestmentsInEntity (String risk); // ejbSelect public abstract Collection ejbSelectInvestments(String risk); ... }

在上述声明中,两种选择方法在不同范围内运行。ejbSelectInvestmentsInEntity() 仅在当前的 Employee bean 例如,它只返回员工的风险投资。

SELECT invest FROM invest IN benefit.investments WHERE invest.type = ?1

另一方面,ejbSelect<METHOD> 该方法的范围是全球性的,因此同一查询将返回整个企业所有员工的所有风险投资。

ejbSelect<METHOD>InEntity 可以返回选择的方法 bean 远程类型(如上述查询)、从属对象或任何其他对象 Java 类型。另一方面,全球选择方法不能返回 bean 从属对象类型。

选择方法的 EJB QL 句子需要使用 SELECT 因为它们可以返回更广泛的各种值。

新的 ejbHome 方法 在 EJB 2.0 中,实体 bean 可以声明一些 ejbHome 该方法用于执行和执行 EJB 与组件相关的操作,但不是专门用于某个操作 bean 实例。在 bean 类中定义的 ejbHome 该方法必须在本地接口中有一种匹配的本地方法。下面的代码描述了一种本地方法,它被用作 Employee bean 定义本地接口。applyCola() 根据最近的情况,使用该方法 COLA(生活费用调整)增长以更新所有员工的工资水。

public interface EmployeeHome extends javax.ejb.EJBHome { // 本地方法 public void applyCola(double increate) throws RemoteException; ... }

applyCola() 方法在 bean 必须有类中的匹配 ejbHome 方法,它被声明为 ejbHomeApplyCola()。ejbHomeApplyCola() 方法不是专门用于一个 bean 例如,它的范围是全局的,所以它将使用所有员工的工资相同 COLA。

public abstract class EmployeeBean implements javax.ejb.EntityBean { ... // ejbHome 方法 public void ejbHomeApplyCola (double increase ){ Collection col = ejbSelectAllEmployees (); Iterator employees = col.iterator(); while(employees.next()){ Employee emp = (Employee)employees.next(); double salary = emp.getAnnualSalary(); salary = salary + (salary*increase); emp.setAnnualSalary(salary); } } }

bean 开发人员需要 BMP 和 CMP 实体 bean 都实现 ejbHome 方法。CMP 在很大程度上,实现可能取决于全局的选择句子(如上所述)和 finder 方法,而 ejbHome 的 BMP 实现了直接数据库的访问和使用 bean 的 finder 查询数据并更改方法。

MessageDrivenBean 在 EJB 2.0 在规范的基本变化中,增加了一个新的企业级 bean 类型,即 MessageDrivenBean。MessageDrivenBean 专门设计处理入网的专项设计 JMS 消息。对许多开发人员来说,JMS 这是一个新的例子,所以本文将花一些时间逐步解释它 JMS 理解,以及它们在 EJB 2.0 中的用法。

什么是 JMS? JMS 它与制造商无关 API,访问新闻收发系统。它类似于 JDBC (Java Database Connectivity):这里,JDBC 它可以用来访问许多不同关系数据库 API,而 JMS 通过访问信息收发服务,提供与制造商无关的访问方法。现在很多厂家都支持 JMS,包括 IBM 的 MQSeries、BEA 的 Weblogic JMS service 和 Progress 的 SonicMQ,这只是几个例子。

JMS 使您能通过消息收发服务(有时称为消息中介程序或路由器)从一个 JMS 另一个客户机 JML 客户机发送消息。消息是 JMS 一种类型的对象由报头和消息主体两部分组成。报头由路由信息和元数据组成。信息主体携带应用程序的数据或有效负载。根据有效负载的类型,新闻可以分为几种类型,分别携带:简单的文本 (TextMessage)、对象可以序列化 (ObjectMessage)、属性集合 (MapMessage)、字节流 (BytesMessage)、原始值流 (StreamMessage),有没有有效负载的消息? (Message)。

新闻收发系统是异步的,也就是说,JMS 客户机可以在不等待回应的情况下发送消息。可以看出,这与基础完全不同 RPC (基于远程过程)系统,如 EJB 1.1、CORBA 和 Java RMI 实现引用。在 RPC 在中间,客户机在服务器上调用分布式对象的方法。在方法调用返回之前,客户机被堵塞;在执行下一个指令之前,客户机必须等待方法调用结束。在 JMS 在此过程中,客户机将消息发送到虚拟通道(主题或队列),而其他消息则发送到虚拟通道 JMS 客户机预订或监控虚拟通道。当 JMS 当客户机发送消息时,它不会等待回应。它执行发送操作,然后继续执行下一个指令。这些客户机可能最终转发到一个或多个客户机,不需要回应。

EJB 2.0 中的 JMS EJB 2.0 以两种方式支持 JMS 集成:作为一种 bean 作为一种可用的资源 MessageDrivenBean。当将 JMS 作为一种资源使用时, JMS API 的 bean 是消息的产生者或发送者。在这种情况下,bean 将消息发送到虚拟通道,称为主题或队列。另一方面,MessageDrivenBean 它是消息的用户或接收者。它监控特定的虚拟通道(主题或队列),并处理发送给该通道的信息。为了更好地理解消息生成者和消息用户的作用,使用它们 SessionBean bean 发送一个使用 JMS 使用新消息,然后使用新消息 MessageDrivenBean 使用同样的信息。

作为 EJB 2.0 资源的 JMS 会话 bean 和实体 bean 都是基于 RPC 这是一个优秀的系统结构,用于组装各种事务组件。但是,在某些情况下,RPC 同步性质会成为障碍,这正是 EJB 1.1 中将对 JMS API 访问是包括资源在内的原因。利用 JNDI 上下文以环境命名,bean 能得到一个 JMS 工厂向主题或队列发送异步消息(也从 JNDI 获得),而不是等待回应。下面是 ShoppingCart bean 它使用的一个例子 JMS 将 Order 发送给新闻收发主题的详细信息。

public class ShoppingCartBean implements SessionBean { // 订单详细信息是包含所有订单信息的可序列化对象。 public OrderDetail orderDetail; public void processOrder(){ // 从这里开始处理订单的逻辑 ... // ... 订单处理后,向其他系统发送此订单的消息 InitialContext jndiEnc = new InitialContext(); // 使用 JNDI ENC 获取 JMS 工厂和主题标识符 TopicConnectionFactory factory = jndiEnc.lookup("java:comp/env/jms/topicfactory"); Topic orderTopic = jndiEnc.lookup("java:comp/env/jms/ordertopic"); // 获得用于发送消息的发布者 TopicConnection con = factory.createTopicConnection(); TopicSession session = con.createTopicSession(false, Session.AUTO_ACKNOWLEDGE ); TopicPublisher publisher = session.createPublisher(orderTopic); // 将一个 ObjectMessage 发送给主题(虚拟通道) ObjectMessage message = session.createObjectMessage(); message.setObject(orderDetail); publisher.publish(message); con.close(); } ... }

在这种情况下,JMS 它被用来通知其他应用程序,订单已经处理好了。这些其他应用程序对处理订单并不重要,但它们将受益于获得已处理订单的通知。这些例子包括自动调整库存的库存系统和可以将客户添加到产品目录邮寄列表中的销售应用程序。

使用 JMS 使 bean 可以在不堵塞的情况下发布(发送)消息。bean 我不知道谁会收到消息,因为它把消息发送到一个主题(虚拟通道),而不是直接发送到另一个应用程序。应用程序可以选择预订主题,并收到关于新订单的通知。这样,就有可能在虚拟通道中动态地添加或删除应用程序,从而产生更灵活的系统。

预订订订单主题的应用程序将收到关于新订单的通知,应用程序可以使用任何他们认为合适的方式来处理通知。从各个队列预订各种主题的应用程序或接收消息的应用程序可以是 Java 应用程序、EAI 系统(用于集成遗留系统和 ERP 系统)或者 MessageDrivenBean 组件,在 JMS 所有的术语都被认为是 JMS 客户机。

JMS 和 MessageDrivenBean 虽然大多数 JMS 制造商提供从发送者路由到接收者的消息中介工具,但构建使用(接收)消息 JMS 客户机是应用程序开发人员的职责。在许多情况下,接收信息的应用程序必须强大、安全、快速、可伸缩;它基本上需要基础设施 EJB 应用程序是一样的。

因为认识到这种需要,EJB 2.0 现在包括了 MessageDrivenBean 它可以使用类型 JMS 消息,并在同一个强大的、基于组件的基础结构中处理这些消息,这样的基础结构对于对话 bean 和实体 bean 都很有用。MessageDrivenBean 类型(消息 bean)是企业级的 bean 组件,它设计用异步设计 JMS 消息。

除提供容器基础结构外,EJB 还有另一个重要的优点:并发处理。在 EJB 在中间,已部署的消息 bean 表示一个单一的消息用户,但是这个 bean 有很多原因 bean 实例提供服务。每个 bean 例子可以单独使用消息 bean 收到的信息。这意味着,新闻 bean 不必像常规 JMS 像客户机一样连续使用消息。消息 bean 多个收到的消息可以并发使用,从而达到比传统更好的效果 JMS 应用程序的吞吐量要高得多,可伸缩性要好得多。

为了解释这个消息 bean 作用,开发出来了 MarketingBean 类,并将其从订单主题部署到供使用的消息中。MarketingBean 从消息中提取 OrderDetail 使用它将客户添加到合适的目录邮寄列表中。这是最精致的大量邮寄系统。

下面是 MarketingBean 类别的定义,这个类别的使用发布给订单主题的信息。

public class MarketingBean implements javax.ejb.MessageDrivenBean { public void onMessage(Message message) { ObjectMessage orderMessage = (ObjectMessage)orderMessage: OrderDetail orderDetail = (OrderDetail)orderMessage.getObject(); Integer customerID = orderDetail.getCustomerID(); InitialContext jndiEnc = new InitialContext(); CatalogHome catalogHome = (CatalogHome)jndiEnc.lookup("java:comp/env/ejb/catalog"); Iterator productIDs = orderDetail.getProductsIDs(); while(productIDs.hasNext()){ Integer productID = (Integer)productIDs.next(); Catalog cat = CatalogHome.findByProductID(productID); cat.addCustomerToMailingList(customerID); } } }

正像会话 bean 和实体 bean 一样,MessageDrivenBean 也是一个完整的企业级 bean,但仍有一些重要的区别。消息 bean 没有远程接口或本地接口。这是因为消息 bean 不是 RPC 组件。它没有供 EJB 客户机调用的业务方法。消息 bean 监控虚拟信息通道(主题或队列)并使用其他信息 JMS 客户机发送给该通道的信息。

各个消息 bean 构成一个 bean 类,这类实现 MessageDrivenBean 接口和一个 XML 部署描述符。以下是 MessageDrivenBean 界面的定义,所有新闻 bean 这个接口必须实现。

package javax.ejb; import javax.jms.Message; import javax.jms.MessageListener; public interface MessageDrivenBean extends MessageListener{ public void onMessage(Message message); public void ejbCreate(); public void ejbRemove(); public void setMessageDrivenContext(MessageDrivenContext mdc); }

当消息驱动部署时 bean 之后,它被指派来处理特定主题或队列中的消息。JMS 客户机(Java 应用程序、bean 或本地客户机)发送的任何消息将由消息路由器转发给消息 bean,该消息 bean 被指派从虚拟通道接收消息。当一条消息被发送给一条消息时 bean 时,EJB 容器将从某个池中选择 bean 一个例子来处理这个消息。当 bean 实例调用其 onMessage() 方法上,它会收到这个消息,并且可以以任何其认为合适的方式来处理这个消息。一旦使用了这个消息,只要事务没有异常停止,这个消息就不会传递给这个消息 bean 任何其他例子。

消息 bean 在某一点上,类似于无状态的会话 bean,即这两种 bean 两个请求之间没有保持任何状态。因此,新闻驱动 bean 是无状态的,但就像无状态的对话一样 bean 同样,它们也可以有实例变量,这些变量在这里 bean 在整个生存期内保持实例。

对消息 bean 最后一点是,理解这样一个事实是非常重要的,即 bean 使用的消息不一定是由其他消息使用的 bean 所产生的。消息 bean 由符合使用 JMS 制造商提供的任何主题或队列中的信息。消息 bean 使用的消息可以来自其他消息 bean(会话 bean、实体 bean 或消息 bean)、非 EJB 的 Java 应用程序,甚至非应用程序 Java 应用程序(如果供应商符合要求 JMS)。例如,可以使用遗留应用程序 IBM 的 MQSeries 向队列发送消息,可用于其他遗留应用程序,也可用于消息 bean 使用。

结论 与以前的规范相比,Enterprise JavaBeans 2.0 中间有一些相当大的变化。新的 CMP 模型比以前的模型灵活得多,允许各种实体为复杂的对象图建立模型,并提供更大的跨容器可移植性。人们渴望定义一种通用的查询语言来寻找和选择操作,这也将有助于提高可移植性。

这种新的 MessageDrivenBean 类型将有助于使这种强大的新闻收发模式成为公众关注的焦点,就像 EJB 那样。新闻收发是分布式混合计算中极其重要的组成部分,包括在内 EJB 内在是其重要性的证明。

写这篇文章时,EJB 2.0 刚刚作为公开草案发布,这意味着在它成为最终规范之前仍有可能发生变化。如果这些变化对这里提供的材料有重大影响,我会试着对这篇文章做一些评论,但这个规范往往是稳定的,所以不太可能有真正的重大变化。