快捷搜索:

全面研读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 字段就不包括在容器治理的字段的列表中,是以它不是持久性字段。

EmployeeEJB

...

Container

...

identity

firstName

lastName

salary

address

...

容器供给者供给一种对象,用来将 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 支配描述符中,抽象的持久性规划声明容器治理的各个字段和各类关系。

EmployeeEJB

...

Container

...

identity

firstName

lastName

...

ContactInfo

ContactInfo

street

city

state

zip

homePhone

workPhone

email

...

Employee-ContactInfo

employee-has-contactinfo

one

EmployeeEJB

contactInfo

ContactInfo

contactinfo_belongsto_employee

one

ContactInfo

用来描述容器治理的关系的 XML 元素可能变得异常繁杂,由于他们必须处置惩罚各类关系的对应性和偏向(单向的照样双向的)。上面的代码段阐明,为了描述 bean 与其从属工具类之间的简单关系,您必要哪些元素。虽然纵然是简单的关系也会被转换为冗长的 XML,但所有这些元素都是必需的,以便持久性治理器能够将繁杂的工具图映射到数据库中。

虽然用于定义 CMP bean 的抽象持久性规划的 XML 元素是 EJB 2.0 中的 CMP 的主要问题,但为了简洁起见,本文不再供给 XML 示例。作为替代,本文将纯挚寄托 bean 类中必须应用的抽象习语,来阐明 EJB 2.0 中的 CMP 背后的基础观点。这些代码习语与 XML 支配描述符中的关系元素一路应用,并由后者定义,以是您不能只有其一而没有另一个,但它们比该规划的 XML 部分较轻易理解。

除了 XML 元素之外,抽象的持久性规划还定义了一组习语,它们在声明 bean 类及其相关的工具时一定会用到。用来造访和改动字段的措施是严格定义了的,要求用 set 措施改动持久性字段,而用 get 措施造访它们。这些措施的名称和返回类型由支配描述符中它们响应的 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 和 ejbSelectInEntity。ejbSelect 措施是全局履行的,这是指这种措施并非专用于履行该措施的 bean 实例。ejbSelectInEntity 措施则专用于履行该措施的实体实例。这些选择措施在 bean 类中被声明为抽象措施,并在这些类的营业措施中应用。下面是 ejbSelect 措施和 ejbSelectInEntity 措施的示例,同时阐清楚明了可以若何在营业措施中应用它们。

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 措施的范围则是全局性的,以是同一个查询将返回全部企业内所有雇员的整个风险投资。

ejbSelectInEntity 选择措施可以返回 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 刚刚作为公开草案宣布,这意味着在它成为一个终极规范之前仍有可能变动。假如变动对此处供给的材料有重大年夜影响,届时我将设法对本文作一些注释,但这个规范正在趋于稳定,以是不太可能有真正重大年夜的变动。

参考资料

"A Beginner´s Guide to Enterprise JavaBeans," Mark Johnson(JavaWorld,1998 年 10 月):

Richard Monson-Haefel 的 EJB 开拓者网站,EJBNow.com

EJB 2.0,规范

Thought 的 CocoBase

IBM 的 MQ Series

BEA 的 WebLogic JMS Service

Progess Sonic MQ

Richard Monson-Haefel 所写的其它文章:

"Create forward-compatible beans in EJB, Part 1"(JavaWorld,1999 年 12 月)

"Create forward-compatible beans in EJB, Part 2"(JavaWorld,2000 年 1 月)

作者简介

Richard Monson-Haefel 是近来宣布的 Enterprise JavaBeans 第二版的作者。他是 OpenEJB 的首席设计师(OpenEJB 是一种开放源代码的 Enterprise JavaBeans 2.0 容器),他曾经以设计师身份为 Enterprise JavaBeans、CORBA、Java RMI 以及其它 Java 规划供给咨询。Monson-Haefel 还掩护着一个网站,供人们评论争论 Enterprise JavaBeans 和相关的散播式谋略技巧。可以经由过程 richard.monson-haefel@javaworld.com 与 Richard Monson-Haefel 联系。

您可能还会对下面的文章感兴趣: