继续贴完上回的翻译:EJB Design Patterns

sean_gao 2004-05-11 12:59:44
上次的帖子结掉了。不过既然已经翻译完第一章,索性把没有贴完的部分也贴出来大家共享。
...全文
93 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
老土豆T 2004-05-11
  • 打赏
  • 举报
回复
哈,好啊...:) 支持...
yourworking 2004-05-11
  • 打赏
  • 举报
回复
mark
chashui 2004-05-11
  • 打赏
  • 举报
回复
这么个贴法倒不如提供个下载的链接 呵呵
sean_gao 2004-05-11
  • 打赏
  • 举报
回复
http://expert.csdn.net/Expert/topic/3021/3021772.xml?temp=.1812555
http://expert.csdn.net/Expert/topic/3023/3023706.xml?temp=.7885858
KillAllError 2004-05-11
  • 打赏
  • 举报
回复
上次的帖子呢!
崇拜搂猪,把上次的帖子连接,贴出来吧
binlinux 2004-05-11
  • 打赏
  • 举报
回复
1
sean_gao 2004-05-11
  • 打赏
  • 举报
回复
翻译得不好,献丑了。
sean_gao 2004-05-11
  • 打赏
  • 举报
回复
---------------------------------------------------------------------------
商务接口模式
---------------------------------------------------------------------------

EJB规范规定企业级bean类可以提供所有在远程/本地接口中声明的方法的实现,但是bean
并不能直接implement这些接口。

[如何在编译期发现远程/本地接口中声明的的方法和实际实现的方法的非连贯一致性?]

* * *

在EJB开发进程中会遇到的一个最常见的错误是在远程或本地接口的商务方法定义与EJB类
的具体实现之间缺乏连贯性。EJB规范要求企业bean正确地实现所有在远程/本地接口中声
明的商务方法,但是并没有提供任何自动化的方式在编译期检测这方面的错误。很多错误
因为这个接口定义和实现的低耦合性而产生,包括输错的方法名、参数类型、异常、非连
贯的参数等等。结果是这些错误无法在编译期被发现,EJB开发者需要手工的维护接口定
义和bean实现之间的连贯性。

这些错误只能通过使用你的EJB服务器供应商的专利后编译工具来检测到。这些工具通常
被用于在打包和发布之前检测编译好的Java类是否符合EJB规范。这些后编译工具通常很
慢也很难使用,并且对于开发人员经常使用的增量编译以提早发现问题的实践也没有很切
实可行的办法。结果是在开发进程的稍后阶段才能发现问题。

一个可能是解决方案是让企业bean直接在类中implement远程或本地接口。这样会确保方法
定义和实现的连贯性,任何Java编译器都能做到。不幸的是,EJB规范建议不要这样做,而
且理由充分。远程接口扩展javax.ejb.EJBObject接口,本地接口实现javax.ejb.
EJBLocalObject接口,如图1.15所示。这些接口定义额外的方法(isIdentical、
getPrimaryKey、remove等等),这些方法是打算由EJBObject和EJBLocalObject的stub
(探针?插入的代码块?)实现而不是由企业bean类实现。

<图略>

为了使你的bean成功编译,你需要往你的企业bean类中塞入这些额外方法的傻瓜实现。并且,
如果企业bean类直接实现了远程或本地接口,bean可以被直接类型转换至其中一个接口,这
样就允许开发人员把this传递给客户端。这种行为是EJB规范所不允许的。要传递对自身的
引用,企业bean需要首先通过调用SessionContext或者EntityContext接口的getEJBObject
或者getEJBLocalObject方法取得对自己的引用。

EJB开发人员不应该在企业bean类中直接implement远程或本地接口,但是我们需要一种机制
在编译期确定远程/本地接口的方法定义和bean类中具体实现的连贯性。

因此:

[创建一个名为商务接口的超级接口,定义所有商务方法。让远程/本地接口和企业bean类都
实现这个接口,促使编译期的连贯一致性检查。]

商务接口是一个普通的Java接口,定义了企业bean选择暴露的所有方法定义。包括远程或本
地接口还有企业bean类都实现这个接口,如图1.16所示。通过构建这样一个超级接口,由远
程/本地接口中的方法定义和企业bean类的不连贯一致产生的错误就会在编译期得到捕获。

这个商务接口并不实现javax.ejb.EJBObject和javax.ejb.EJBLocalObject接口,于是
bean类的开发人员不需要实现傻瓜方法。并且,开发人员不能将bean类直接类型转换到它的
远程或者本地接口,防止bean开发人员将this传递给客户端。

<图略>

商务接口模式根据企业bean是在本地接口中还是在远程接口中暴露其商务方法而略有不同。
如果实体bean暴露其远程接口,商务接口中所有的方法定义都需要指明抛出java.rmi.
RemoteException异常(但不需要扩展java.rmi.Remote)。注意这些方法在企业bean类中
的实现并不应抛出RemoteException,因为这个被EJB规范定义为过期的。商务方法可以在
方法体中抛出EJBException,且不需要声明throws,因为EJBException是RuntimeException
的子类。

当与本地接口一起使用商务接口时,商务接口不需要实现任何其他的接口,商务方法的定义
也不需要区别对待。

使用商务接口模式有一个危险的附产物。对于那些返回值为bean本身的远程/本地接口类型的
方法而言,实现这个商务接口可以让bean开发者在没有任何编译期问题被检测到的情况下返
回this。这是可能的,因为不管是bean类还是元成/本地接口都实现了商务接口。当没有使用
商务接口模式时,返回this总是会被在编译期发现(因为bean类没有implement远程/本地接
口)。因此,我们在使用商务接口模式时必须特别注意不要返回this,不然运行可可能出现
不可预期的错误。

商务接口模式是EJB开发中常见的模式。它允许开发人员在编译期发现通常的编程错误,保证
商务方法定义和实现的一致连贯性。

sean_gao 2004-05-11
  • 打赏
  • 举报
回复
在CMP中,我们无法使用内部的属性映射表,因为实体bean的类的实现被抽象到了容器生
成的get/set方法背后。当使用内部映射表不可能的时候,我们可以通过Java Reflection
API实现属性访问接口。换句话说,在setAttribute中,我们可以对客户端希望设定的属
性的键值执行反射。具体讲,如果键值为XXX,则在setAttribute方法的实现中将会尝试
调用实体bean的setXXX(...)方法。同样,在getAttributes方法中,我们可以利用反射找
到实体bean的所有get方法,调用它们,然后填充一个映射表给客户端。如果我们更倾向
于不使用Replection API,那么对于CMP来讲将无法实现AttributeAccess接口的方法。这
样的话我们就需要大量使用IF语句作判断,硬编码调用实体bean的get/set方法。

为了使AttributeAccess的实现可以在所有的实体bean中被重用,我们可以使用一个基类来
实现这个接口的方法,这个实现是一般性的,而不管我们是用反射还是内部属性表来实现。
任何需要利用AtrributeAccess模式的实体bean只需要从基类继承就可以了,这样就自动的
暴露对其属性的统一的接口,而并不需要额外的编码。

最后一块迷题是怎样命名键,这些键名用于标识实体bean的属性。不仅仅属性需要由这些
键来标识,通过属性访问接口的实体bean和实体bean自身都需要统一命名规则。某种意义
上讲这是一个"契约",无论客户端还是服务器端都需要遵守。以下是几种可能的情况:

[建立一套连贯的、翔实而具体的命名规范] 客户端和实体bean可以共同遵守一个翔实而具
体的、连贯一致的属性命名规范。对Account实体bean,"com.bank.Account.accountName"
或者简单的"accountName"都是连贯一致的范例。这样做的缺点是这个契约仅仅存在于开发
者脑中和文档中。开发过程中,我们很容易拼错属性名,造成严重的、难以查找的开发错误。

[在实体bean的远程或者本地接口中定义static final的成员变量] 实体bean客户可以在调
用过程中引用这些static final的变量,这些变量包含了用于获取属性的正确的键字符串。
比方说,为了从一个Employee实体bean中取得属性,会话bean可以使用如下代码:

<代码略>

这种方式对DTO工厂模式来说运作非常好,会话bean直接向实体bean查询其属性,返回硬编码
的数据传输对象而不是HashMap。在这里只有会话bean和实体bean需要统一属性键名,本地/
远程接口很适合包含属性的名称。当使用数据传输HashMap模式时,这种方法就失效了,因为
客户端也需要知道键-值的名称,而客户端无法访问实体bean的远程/本地接口。

[包含了static final成员变量的共享类] 我们可以创建一个供客户端和服务器端共享的类。
这个类用于封装了用于填充和从HashMap取值的具体字符串的类,这些字符串被硬编码作final
static的变量,这些变量可以被客户端和服务器端访问。举例说,客户端可以从哈希映射表中
查询属性如下:

<代码略>

这种方法的缺陷是一旦键映射关系需要更新或者增加,新的类需要被重新发布到客户端和服务
器端(它们的JVM于是需要重启)。

[将属性契约放到JDNI树中] 这个方法将包含键名的类放到JNDI树中,因而这个类可以被客户
端和服务器端共享。当键名发生变化时,客户端和服务器端的代码因而不需要重新编译或者重
启,因为一个中央的JDNI树中的对象保有最新的键名。这个方案的缺陷在于每次需要键值的时
候都需要从JDNI中查找契约,因而会带来额外开销。

通用属性访问模式有许多优点:

[对所有实体bean只有一个接口] 实体bean客户可以通过属性访问接口连贯一致的操作实体bean,
这样就简化了客户端代码。实体bean也简化了,因为属性访问可以被封装在基类中。

[对大型的实体bean同样适用] 不管实体bean包含20个还是2000个属性,属性访问逻辑都被简化
成简单的几行。

[随着时间的推移产生的维护费用低] 在不需要对服务器端任何修改的情况下可以创建对服务器
端数据的新视图。客户端可以动态决定需要显示那些属性。

[允许运行期动态增加属性] 使用BMP时,这个模式很容易扩展以允许动态的增加或去除实体bean
的属性。我们可以网接口中增加addAttribute和removeAttribute方法,这些方法只需要简单的
对属性HashMap进行操作。

跟其他所有模式一样,使用通用属性访问模式也有其弊端:

[对每次方法调用的额外开销] 对每次属性调用,客户端都必须使用属性键来标识属性。最终,
在从HashMap中取出后,属性都需要转换成其正确的类型。

[需要维护属性键的契约] 因为属性是通过字符串取得的,客户端需要记住用于标识属性的键字
符串。我们可以通过定义一套键-属性的契约(在这个模式中先前介绍过)来缓解这种依赖性。

[没有了强类型/编译期检查] 当我们使用DTO的时候,通过get/set传递的值类型总是正确的;
任何错误都会在编译期发现。当我们使用通用属性访问模式时,属性访问需要客户端在运行时转
换成正确的类型以及将正确类型的属性对应到正确的键。

总的说来,通用属性访问模式提供了一个一般性的管理实体bean状态的方法,消除了域相关的实
体bean数据访问相关的大量重复代码。

[相关模式]

属性容器(Carey, et al., 2000)
数据传输HashMap
sean_gao 2004-05-11
  • 打赏
  • 举报
回复
---------------------------------------------------------------------------
通用属性访问模式
---------------------------------------------------------------------------

实体bean的客户需要访问实体bean的属性。

[实体bean的客户怎样有效的以一种整体的、通用的方式操纵实体bean的属性?]

* * *

在通常的实践中,我们通过本地接口的get/set方法(对于EJB2.X及以上的实体bean)
或者通过远程接口的整体的数据传输对象(对于EJB1.X的实体bean)来访问实体bean。
前者中,我们通过会话面板或者数据传输对象工厂的方法与实体bean交互,调用多个
细化的属性取值方法或赋值方法来根据特定用例的要求访问和操纵属性。在后者中,
我们使用数据传输对象以一种整体的方式来访问和操纵bean的状态,以便最大限度减
少通过远程接口的网络调用。使用DTO来操纵实体bean是优化与EJB 1.X实体bean通信
的常见模式。

使用DTO来访问EJB 1.X实体bean的弊端是会削弱实体bean层的可维护性(见DTO工厂模
式)。EJB 2.0出现以后,本地接口允许将DTO的创建和使用逻辑提取出来放到一个数
据传输对象工厂中,在这里DTO工厂通过实体bean本地接口的细化的get/set方法与实
体bean交互,解决了使用DTO的一些问题。

不幸的是,EJB 1.X实体bean没有本地接口。这就造成了DTO的创建/使用逻辑不能从实
体bean中提取出来放到DTO工厂中(因为DTO工厂通过实体bean的远程接口进行多次细
化的调用会严重影响性能)。我们需要一种其他的允许通过远程接口整体访问实体bean
数据的机制,同时不往其中塞满DTO创建/使用逻辑。

甚至对于本地接口,有时候暴露多个get/set方法也不是个好的主意:

[并非对所有大小的实体bean都适合] 想象一个财务应用程序中的证券/债券实体bean。
这样一个实体bean可能拥有200个以上的属性。为所有这些属性编写和暴露属性取值/赋
值方法可能会造成噩梦或者冗长乏味的编写过程以及接口大小的急速增长。

[会产生高度耦合的、硬编码的客户端] 实体bean客户(比如会话面板)需要紧密地同
实体bean的接口耦合在一起,使得它必须面临分分钟都会出现的改变,如增加或者去掉
一个属性。

讨论的结果是我们需要找到另外的方法访问实体bean数据,一个允许DTO工厂利用远程
接口在一个较大的网络调用中动态获取不同的实体bean状态的子集,同时在使用本地
接口时将实体bean的客户同实体bean的属性访问方法分隔开来的方法。

因此:

[将实体bean的属性访问逻辑抽象出来放到一个通用的属性访问接口中,使用HashMap
传递属性进出实体bean。]

实体bean通过远程或者本地接口来实现这个属性访问接口,看上去是这个样子:

<代码略>

属性访问模式提供一个通用的接口允许从实体bean动态的get/set任意的数据集。这个
接口允许EJB 1.X的实体bean使用DTO工厂将DTO创建/使用逻辑提取出来并优化远程调
用,同时允许EJB 2.X的实体bean简化其本地接口:去掉细化的get/set方法。客户和
实体bean代码之间唯一的依赖性是用于确定属性的键名,这个在本模式稍后介绍。

会话面板或者DTO工厂可以用过这个属性访问接口访问实体bean的属性。图1.14所示为
一个典型的案例。客户端感兴趣的是Car实体bean数据中的有关引擎的子集。客户端调
用会话面板的getCarEngineData方法,这个方法向实体bean查询确切的与汽车引擎相
关的属性:首先创建一个包含相关属性(马力、排量等)的键的集合,然后传递这个
集合到实体bean的getAttributes(collection)方法,该方法返回确切的包含这些键的
HashMap。

从Car实体bean中接收到填充好的HsahMap对象后,会话bean可以:

[将这个HashMap返回给一个远程客户] 这时会话bean使用HashMap作为可序列化的容器
用于在网络中传输数据(在第二章中的数据传输HashMap模式中有讲)。

[将该HashMap转换成DTO然后返回] 作为一个DTO工厂,会话bean可以将值从HashMap中
提取出来加入到数据传输对象中,返回这个DTO给客户端。

<图略>

选择哪一种方式,由开发者决定。作为数据传输的方式,HashMap提供了许多优于数据
对象(在数据传输对象模式中有讲)的特点,同时也带来了明显的额外复杂度。如果属
性访问用在DTO工厂背后,键/值之间的依赖性可以保持在服务器端,在这里会话bean反
正都有必要知道实体bean的信息。

通过使用属性访问接口,会话bean就可以在运行期动态的决定需要哪些实体bean数据的
子集,从而避免了手工的对数据传输对象的设计。

和接口本身一样,实现属性访问接口的方法也是一致通用的。在BMP中,我们可以将它
所有的属性储存到一个内部的私有HashMap中,而不是通常的直观的硬编码属性,从而
更进一步简化。对于大型的实体bean来说,这个优化可以在很大程度上简化bean的代码。
通过使用内部的HashMap,AttributeAccess接口方法的实现变得完全通用和跨BMP实体bean:

<代码略>

67,513

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧