这是原文翻译过来的
原文出处: Effective EJB: Make EJBs Work For You
Java开发正处于一个十字路口。开放的标准已经为Java平台和语言带来了很多益处,但它们也带来了一些问题。开发人员经常对Java开发的复杂性感到头疼。更糟糕的是,复杂度是如此严重,以至于实际的业务问题反而处于次要地位。
J2EE规范提供了许多API、标准和开放的终端,允许架构师、设计师和开发人员建立出众的企业系统。在权衡选择适当技术时必须小心。
技术的发展为开发人员带来的更多是困惑(除非开发人员是无所不能的),而不是帮助他们解决这些问题。通常情况下,架构师和开发人员将大多数时间花在对选中框架的支持上,而不是用来解决手头的业务问题。
本文将讨论在使用Enterprise Java Beans (EJB)规范设计和开发出众的J2EE应用程序的过程中所涉及到的技术。尽管本文的目的不是展示如何使用EJB,但是我还是会谈到EJB开发过程中的各种陷阱,主要集中于现实世界中的一些反模式,这些反模式已经偷偷进入您的开发过程中。
按部就班地学习固然不错,但是由于日益缩短的开发周期,从其他人的错误中学习是更明智的方法。对常出现危险的位置的全面了解将有助于采用主动的策略,而这正是本文的主题。我们将从J2EE应用程序的EJB上下文开始,并讨论存在于EJB开发中的一些潜在危险。
软件是一种设计艺术,并且设计是不断延伸的。随着每种激动人心的技术(如:EJB规范)的出现,疲倦的工程师往往都急切地采用新技术。这是大多数EJB项目会失败的原因之一。通常它们一败涂地,以至于得不到任何回报。这样更明智一些:慎重对待EJB的抉择,而不是仅仅因为它是一种很热门、很有吸引力、有前途的技术就采用它。
论及用于构建分布式的事务型长期业务解决方案的组件架构,要决定是否要在软件项目中使用EJB,需要用良好的设计实践进行认真的分析和主动的规划。
“当您手中有一把锤子时,任何东西看上去都像钉子。”这个谚语适用于大多数EJB选择。对于大多数项目来说,EJB可能不是最佳选择,因为存在如下情况:
图1 展示了在项目规模和成本方面,简单的POJO(Plain Old Java Objects)解决方案与EJB解决方案之间的折衷。
在初学者和中等水平的用户中间最常见的对EJB的误解是,如果使用EJB的话,每个组件都是一个EJB。
错了,这种判断很少是成立的。某个组件或子系统可能真的是候选的EJB,但是其他组件或子系统可能只是POJO。这种混合模式的设计将很难进行,但是慎重对待每个组件/子系统级的设计决策可以让转出时的工作变得比较容易。
在子系统级选择EJB的一个重要问题是理解EJB服务。EJB组件呈现两种风格:一组作坊组件,称为会话bean;以及一组持久性组件,称为实体bean。会话bean通常是粗粒度服务,利用容器的功能提供分发、事务管理和安全性。对于非EJB组件,实现这些服务是开发人员的责任。那些大量使用了这些服务的组件通常是会话EJB的正确候选者。
EJB规范通过实体bean实施对细粒度持久性服务的支持。每个实体bean本身也都可以是分布式的、支持事务的、安全的,因此存在细粒度和粗粒度服务的复杂混合。这种混合往往使实体bean组件很难管理。如果只需要一个组件是持久的,那么很少需要将该组件作为EJB。存在其他更易于维护的、流行的轻量级持久性框架(例如:JDO、Hibernate等)。
EJB设计的一个关键问题是创建接口。接口是EJB组件的通用语。它们是将EJB组件所提供的服务暴露给外部世界的手段。拙劣的接口设计将会导致EJB很难维护和修改。
您的网络管道有多大? 到目前为止,EJB采用的还是RMI。它涉及到远程过程调用。位置的透明性只会提高网络的复杂性。EJB调用涉及到通过网络编组和解组参数(非常重要!)。在设计这些远程接口时必须十分小心。如果有一个可以允许大量数据迅速流过的大管道,那么接口可以是粗粒度的。反之,如果拥有的带宽较窄,那么细粒度的接口结合轻量级的参数编组会更好一些。
是否需要外观(façade)?事先应该考虑好是否要使用外观作为其他EJB组件的网关,事后再思考就没有什么意义了。一旦决定使用外观,就可以设计外观来返回作为值的集合的对象。细粒度访问将由外观来处理,并且由于它通常在容器内,所以将会十分有效。这样就可以减少到远程服务器的往返次数。
使用外观。但是如何使用外观才好?在设计EJB接口时,对于设计远程接口而言,关键是要尽量避免状态,而且远程接口使用粗粒度即可。粗粒度接口设计不一定意味着在一个外观中集中EJB层中的所有方法。通常情况下,将多个外观用作EJB组件的网关是比较好的方法。在设计基于EJB的架构时,最安全的方法是在面向对象的域模型与过程远程服务层之间找到一个平衡点。
异常处理是EJB开发中另一个最让人困惑的领域。从本质上来说,异常是对预期行为的偏离。这些中断在分布式架构中非常常见,因为涉及到太多异构环境,而这些环境全都通过网络连接。一个网络故障就能引发一场灾难。EJB中的异常处理特别复杂,而且涉及两种类型的异常。下面是处理异常的一些指导原则。
在开发MDB时,应该特别注意识别可能引起“热土豆”问题的代码区域。其解决方案是将确认和消息处理分为两条执行路径。一接收到消息就立即确认。当在消息处理过程中出现异常时,可能需要将异常写入错误队列。
EJB规范通过一个称为实体bean的特殊bean类来处理持久性服务。实体bean的目标是将域数据模型抽象化。就这一点而言,如果底层的数据源是关系型数据库管理系统,那么实体bean代表的就是数据库表中的行。最初的规范激起了许多关于实体bean及其用法的争论。此后,持久性API在1.1和2.0规范中重写了两次。
由于持久性是一种细粒度服务,它必然不适合EJB服务的粗粒度特性。这带来了一些困惑:如何使用实体bean才最好?在做出使用实体bean的决定之前,建议先对其他持久性模型进行认真的评估。如果决定使用实体bean,那么应该仔细考虑和评估下面的问题。
由于实体bean的细粒度特性,当直接把它们暴露给客户端时,很容易产生反模式。一个最常遇到的问题可能是n + 1问题。它是指为了检索一个业务实体的n个属性,需要n+1次远程调用。额外的一次调用是从EJB容器获得远程存根。通过无格式的POJO DAO实现同样的检索功能会更简单一些。只需用一个JDBC调用来检索行。
一个与暴露实体bean相关的更微妙的问题是事务完整性的缺失。考虑一个具有三个mutator方法的实体bean。进一步假定客户端需要更新事务中两个mutator方法所代表的实体的两列。客户端如何保证实体bean上两个连续的更新方法之间的事务完整性?唯一的方法是使用会话外观来包装实体bean。
在应用程序代码中管理实体关系将会引起严重的性能问题。Java没有针对数据库查找进行过优化。在Java代码内部模拟应用程序连接是一个坏的编程习惯。数据库使用成熟的技术来优化查询访问计划,从而将数据比较的次数降至最少。例如,在应用程序中,给定一个地址实体查找Person实体的动作无法调节。这些类型的关系能够按照CMR字段(源于EJB 2.0)得到最好的表示。应用程序在循环结构中为模拟关系连接而进行的比较的次数将会对应用程序的性能产生重要影响。
用长的主键设计实体bean将会导致性能随着数据存储区中数据的增长而下降。主键被数据库用于查找。数据库索引大量利用主键来建立哈希表。与短主键相比,长主键在建立哈希表的过程中需要更多的计算。数据库缓存索引字段来优化关系连接的性能。长主键往往会在缓存中占用许多空间,从而促生系统失效情况。
然而,多长才算长呢?对于主键可以有多长,不存在基准。长度依赖于目标数据库表中的数据量。
不管在开发过程中是否采用了良好的设计、编码和工程方面的实践,系统都需要进行调优,对于基于EJB的系统来说这尤其正确,因为它们非常复杂,难以进行高级测试。下面是一些性能调优的指导原则。
以灵活性和可伸缩性的名义用XML来填充JMS消息不能解决一切问题。由于XML是新的热门技术,大多数设计师/开发人员往往过度使用XML。当存在其他的灵活选择时,XML的过度使用会严重影响应用程序的可伸缩性。
XML是一项伟大的技术,它将不同种类的环境集中在一起,但最好能理智地使用它。记住,XML也增加了开销。因为XML是无类型的(所有的内容都是字符串),所以必须进行类型转换和检查。此外,分析庞大的XML消息是一个非常耗费资源的过程。XML的过度使用通常出现在JMS实现中。在这种情况下,MDB会花费许多时间进行类型检查并分析收到的XML消息。
EJB规范的目标之一是允许创建可移植组件。然而在现实中,它必须依赖于容器提供者所提供的扩展和增强。例如,每个供应商都有其私有的部署描述符,该描述符允许调优EJB实例的行为。重要的是要认识到,可移植性是一个希望有的功能而不是必需的。企业很少改变应用服务器,很可能要在同一个服务器上部署EJB。因此充分利用容器提供者所提供的额外功能是很明智的做法。
正如前面所讨论的,实体bean本质上不适合分布式服务的粗粒度特性。此外,它们非常耗费资源,而且难以更改和维护。使用实体bean的最初目标是能够在机器A上部署一个域模型,在机器B上部署另一个域模型,使它们能够通过位置透明性进行无缝地互操作。由于通过网络连接,这很少是正确的。这导致开发人员调整实体bean模型,而这只能让事情变得更糟。
随着轻量级容器的逐渐流行,我们有了非常好的提供透明持久性的替代方案。例如,来自于开源领域的Hibernate。它向POJO提供了透明持久性服务。它易于配置,不需要完全的J2EE容器。Sun制定了一个通过JDO实现透明持久性的新标准。尽管JDO还处在初期阶段,并且还没有完全被J2EE供应商所接受,但它提供了许多希望,而且它是一个相当简单的API。
分布式组件是长期投资,它们在公司的IT基础架构中起着很重要的作用。它们被大量使用,一个故障就会引起巨大的损失。必须小心确保中间件组件在生产中运行良好。这可以通过开发生命周期中每一个阶段的严格测试来保证。EJB组件需要专门的测试,因为它们存在于一个托管环境(容器)中。从设计到编码到集成和部署,EJB组件应该使用完全真实的用例进行测试,以便确保更平滑的生产过渡。
在实现通过简化开发来提高开发人员的生产力的承诺方面,EJB架构可能是仅有的败得如此惨重的J2EE组件。EJB 3.0正在通过降低EJB的复杂性再次试图实现这一承诺。
EJB 3.0减少了开发人员需要提供的编程工件的数量,消除了需要实现的回调方法或将其减至最少,降低了实体bean编程模型和O/R映射模型的复杂性。下面是EJB 3.0规范的主要新特性。
早期编程模型的问题之一是EJB需要许多类和描述符。从EJB 3.0开始,不再需要接口或部署描述符。只需一个得到充分注释的EJB类(本质上是一个POJO),为容器提供运行时信息。
从决定使用EJB到生产部署,每一步都需要认真地考虑、分析和规定。本文为EJB开发提供了一些指导原则,并展示了EJB开发中的一些潜在的灰色领域。然而,新的反模式不断出现,这要由架构师、工程师和开发人员来解决。
EJB 3.0确实是向简化EJB开发迈出的一大步,这不仅表现在其编码部分。能够编写简单的EJB将使维护更加方便,并降低生产中的风险。J2EE和扩展的EJB现在是企业应用程序的默认开发平台。
要有效使用EJB,需要理解EJB只是J2EE的一个扩展。J2EE应用程序不一定要使用EJB。但是,在需要时,EJB能够为应用程序提供重要的优势。开发有效EJB的关键是,避开那些说得天花乱坠的虚假宣传,根据EJB本身的能力来评估该技术。这将帮助您做出更好的决策,理智地使用EJB,最终构建有效的EJB。
希望本文能对您有所帮助!
作者简介 | |
Shankar Itchapurapu是CitiGroup Technologies的一位顾问,Mr. Itchapurapu拥有计算机应用硕士学位。 |