最重要的J2EE最佳实践

始终使用 MVC 框架。 

MVC 框架可以将业务逻辑(Java beans 和 EJB 组件)、控制器逻辑(Servlets/Struts 动作)、表示层(JSPXML/XSLT)清晰地分离开来。良好的分层可以带来许多好处。 

MVC 框架对于成功使用 J2EE 是如此重要,以致没有其他最佳实践可以与其相提并论。模型-视图-控制器(MVC)是设计 J2EE 应用程序的基础。MVC 将您的程序代码简单地划分下面几个部分: 

负责业务逻辑的代码(即模型??通常使用 EJB 或者普通的 Java 对象来实现)。 

负责用户界面显示的代码(即视图??通常通过 JSP 及标记库来实现,有时也使用 XML 和 XSLT 来实现)。 

负责应用程序流程的代码(即控制器??通常使用 Java Servlet 或像 Struts 控制器这样的类来实现)。 

如果您不遵循基本的 MVC 框架,在开发过程中就会出现许多的问题。最常见的问题就是在视图部分添加了太多的成分,例如,可能存在使用 JSP 标记来执行数据库访问,或者在 JSP 中进行应用程序的流程控制,这在小规模的应用程序中是比较常见的,但是,随着后期的开发,这样做将会带来问题,因为 JSP 逐步变得越来越难以维护和调试。 

类似地,我们也经常看到将视图层构建到业务逻辑的情况。例如,一个常见的问题就是将在构建视图时使用的 XML 解析技术直接应用到业务层。业务层应该对业务对象??而不是绑定到视图的特定数据表示进行操作。 

然而,只是具有合适的组件并不一定意味着可以使您的应用程序得到合适的分层。我们常常见到一些应用程序包含 servletJSP 和 EJB 组件所有这三项,然而,其主要的业务逻辑却是在 servlet 层实现的,或者应用程序导航是在 JSP 中处理的。您必须进行严格的代码检查并重构您的代码,以确保应用程序的业务逻辑只在模型层(Model layer)进行处理,应用程序导航只通过控制器层(Controller layer)进行处理,而您的视图(Views)只是将传递过来的模型对象以 HTML 及 JavaScript 的形式表示出来。 

在应用程序的每一层都使用自动单元测试和测试管理。 

不要只是测试您的图形用户界面(GUI)。分层的测试使测试及维护工作变得极其简单。 

在过去的几年中,在方法学领域有了相当大的革新,例如新出现的被称为 Agile(例如 SCRUM [Schwaber] 和极限编程 [Beck1])的轻量级方法现在已经得到了很普遍的应用。几乎所有的这些方法中的一个共同的特征是它们都提倡使用自动的测试工具,这些工具可以帮助开发人员用更少的时间进行回归测试 (regression testing),并可以帮助他们避免由于不充分的回归测试造成的错误,因此可以用来提高程序员的工作效率。实际上,还有一种被称为 Test-First Development [Beck2] 的方法,这种方法甚至提倡在开发实际的代码之前就先编写单元测试。然而,在您测试代码之前,您需要将代码分割成一些可测试的片断。一个"大泥球"是难以测试的,因为它不是只实现一个简单的易于识别的功能。如果您的每个代码片断实现多个方面的功能,这样的代码将难以保证其完全的正确性。 

MVC 框架(以及 J2EE 中的 MVC 实现)的一个优点就是元素的组件化能够(实际上,相当的简单)对您的应用程序进行单元测试。因此,您可以方便地对实体 bean、会话 bean 以及 JSP 独立编写测试用例,而不必考虑其他的代码。现在有许多用于 J2EE 测试的框架和工具,这些框架及工具使得这一过程更加简单。例如,JUnit(是一种由 junit.org 开发的开放源代码工具)和 Cactus(由 Apache 开发的开放源代码工具)对于测试 J2EE 组件都非常有用。[Hightower] 详细探讨了如何在 J2EE 中使用这些工具。 

尽管所有这些详述了怎样彻底地测试您的应用程序,但是我们仍然看到一些人认为只要他们测试了 GUI(可能是基于 Web 的 GUI,或者是独立的 Java 应用程序),则他们就全面地测试了整个应用程序。GUI 测试很难达到全面的测试,有以下几种原因。首先,使用 GUI 测试很难彻底地测试到系统的每一条路径,GUI 仅仅是影响系统的一种方式,可能存在后台运算、脚本和各种各样的其他访问点,这也需要进行测试。然而,它们通常并不具有 GUI。第二,GUI 级的测试是一种非常粗粒度的测试。这种测试只是在宏观水平上测试系统的行为。这意味着一旦发现存在问题,则与此问题相关的整个子系统都要进行检查,这使得找出 bug(缺陷)将是非常困难的事情。第三,GUI 测试通常只有在整个开发周期的后期才能很好地得到测试,这是因为只有这那个时候 GUI 才得到完整的定义。这意味着只有在后期才可能发现潜在的 bug。第四,一般的开发人员可能没有自动的 GUI 测试工具。因此,当一个开发人员对代码进行更改时,没有一种简单的方法来重新测试受到影响的子系统。这实际上不利于进行良好的测试。如果开发人员具有自动的代码级单元测试工具,开发人员就能够很容易地运行这些工具以确保所做的更改不会破坏已经存在的功能。最后,如果添加了自动构建功能,则在自动构建过程中添加一个自动的单元测试工具是非常容易的事情。当完成这些设置以后,整个系统就可以有规律地进行重建,并且回归测试几乎不需要人的参与。 

另外,我们必须强调,使用 EJB 和 Web 服务进行分布式的、基于组件的开发使得测试单个的组件变得非常必要。如果没有"GUI"需要测试,您就必须进行低级(lower-level)测试。最好以这种方式开始测试,省得当您将分布式的组件或 Web 服务作为您的应用程序的一部分时,您不得不花费心思重新进行测试。 

总之,通过使用自动的单元测试,能够很快地发现系统的缺陷,并且也易于发现这些缺陷,使得测试工作变得更加系统化,因此整体的质量也得以提高。 

按照规范来进行开发,而不是按照应用服务器来进行开发。 

要将规范熟记于心,如果要背离规范,要经过慎密的考虑后才可以这样做。这是因为当您背离规则的时候,您所做的事情往往并不是您应该做的事情。 

当您要背离 J2EE 可以允许您做的事情的时候,这很容易让使您遭受不幸。我们发现有一些开发人员钻研一些 J2EE 允许之外的东西,他们认为这样做可以"稍微"改善J2EE的性能,而他们最终只会发现这样做会引起严重的性能问题,或者在以后的移植(从一个厂商到另一个厂商,或者是更常见的从一个版本到另一个版本)中会出现问题。实际上,这种移植问题是如此严重,以致 [Beaton] 将此原则称为移植工作的基本最佳实践。 

现在有好几个地方如果不直接使用 J2EE 提供的方法肯定会产生问题。一个常见的例子就是开发人员通过使用 JAAS 模块来替代 J2EE 安全性,而不是使用内置的遵循规范的应用程序服务器机制来进行验证和授权。要注意不要脱离 J2EE 规范提供的验证机制,如果脱离了此规范,这将是系统存在安全漏洞以及厂商兼容性问题的主要原因。类似地,要使用 servlet 和 EJB 规范提供的授权机制,并且如果您要偏离这些规范的话,要确保使用规范定义的 API(例如 getCallerPrincipal())作为实现的基础。通过这种方式,您将能够利用厂商提供的强安全性基础设施,其中,业务要求需要支持复杂的授权规则。 

其他常见的问题包括使用不遵循 J2EE 规范的持久性机制(这使得事务管理变得困难)、在J2EE程序中使用不适当的J2SE 方法(例如线程或 singleton),以及使用您自己的方法解决程序到程序(program-to-program)的通信,而不是使用 J2EE 内在支持的机制(例如 JCAJMS 或 Web 服务)。当您将一个遵循 J2EE 的服务器移植到其他的服务器上,或者移植到相同服务器的新版本上,上述的设计选择将会造成无数的问题。唯一要背离规范的情况是,当一个问题在规范的范围内无法解决的时候。例如,安排执行定时的业务逻辑在 EJB2.1 出现之前是一个问题,在类似这样的情况下,我们建议当有厂商提供的解决方案时就使用厂商提供的解决方案(例如 WebSphere Application Server Enterprise 中的 Scheduler 工具),而在没有厂商提供的解决方案时就使用第三方提供的工具。如果使用厂商提供的解决方案,应用程序的维护以及将其移植到新的规范版本将是厂商的问题,而不是您的问题。 

最后,要注意不要太早地采用新技术。太过于热衷采用还没有集成到 J2EE 规范的其他部分或者还没有集成到厂商的产品中的技术常会带来灾难性的后果。支持是关键的??如果您的厂商不直接支持一种特定的在 JSR 中提出的技术,但此技术还没有被 J2EE 所接受,那么您就不应该采用此技术。毕竟,我们中的大多数人从事解决业务问题,而不是推进技术的发展。 

从一开始就计划使用 J2EE 安全性。 

启用 WebSphere 安全性。这使您的 EJB 和 URL 至少可以让所有授权用户访问。不要问为什么??照着做就是了。 

在与我们合作的客户中,一开始就打算启用 WebSphere J2EE 安全性的顾客是非常少的,这一点一直让我们感到吃惊。据我们估计大约只有 50% 的顾客一开始就打算使用此特性。例如,我们曾与一些大型的金融机构(银行、代理等等)合作过,他们也没有打算启用安全性。幸运的是,这种问题在部署之前的检查时就得以解决

不使用 J2EE 安全性是危险的事情。假设您的应用程序需要安全性(几乎所有的应用程序都需要),您敢打赌您的开发人员能够构建出自己的安全性系统,而这个系统比您从 J2EE 厂商那里买来的更好。这可不是个好的赌注,为分布式的应用程序提供安全性是异常困难的。例如,您需要使用网络安全加密令牌控制对 EJB 的访问。以我们的经验看来,大多数自己构建的安全性系统是不安全的,并且有重大的缺陷,这使产品系统极其脆弱。 

一些不使用 J2EE 安全性的理由包括:担心性能的下降,相信其他的安全性(例如 Netegrity SiteMinder)可以取代 J2EE 安全性,或者是不知道 WebSphere Application Server 安全特性及功能。不要陷入这些陷阱之中,尤其是,尽管像 Netegrity SiteMinder 这样的产品能够提供优秀的安全特性,但是仅仅其自身不可能保护整个 J2EE 应用程序。这些产品必须与 J2EE 应用程序服务器联合起来才可能全面地保护您的系统。 

其他的一种常见的不使用 J2EE 安全性的原因是:基于角色的模型没有提供足够的粒度访问控制以满足复杂的业务规则。尽管事实是这样的,但这也不应该成为不使用 J2EE 安全性的理由。相反地,应该将 J2EE 验证及 J2EE 角色与特定的扩展规则结合起来。如果复杂的业务规则需要做出安全性决策,那就编写相应的代码,其安全性决策要基于可以直接使用的以及可靠的 J2EE 验证信息(用户 ID 和角色)。 

创建您所知道的

反复的开发工作将使您能够逐渐地掌握所有的 J2EE 模块。要从创建小而简单的模块开始而不是从一开始就马上涉及到所有的模块。 

我们必须承认 J2EE 是庞大的体系。如果一个开发小组只是开始使用 J2EE,这将很难一下子就能掌握它。在 J2EE 中有太多的概念和 API 需要掌握。在这种情况下,成功掌握 J2EE 的关键是从简单的步骤开始做起。 

这种方法可以通过在您的应用程序中创建小而简单的模块来得到最好的实现。如果一个开发小组通过创建一个简单的域模型以及后端的持久性机制(也许使用的是 JDBC,并且对其进行了完整的测试,这会增强他们的自信心,于是他们会使用该域模型去掌握使用 servlet 和 JSP 的前端开发。如果一个开发组发现有必要使用 EJB,他们也会类似地开始在容器管理的持久性 EJB 组件之上使用简单的会话 Facades,或者使用基于 JDBC 的数据访问对象(JDBC-based Data Access ObjectsDAO),而不是跳过这些去使用更加复杂的构造(例如消息驱动beanJMS)。 

这种方法并不是什么新方法,但是很少有开发组以这种方式来培养他们的技能。相反地,多数开发组由于尝试马上就构建所有的模块,同时涉及 MVC 中的视图层、模型层和控制器层,这样做的结果是他们往往会陷入进度的压力之中。他们应该考虑一些敏捷(Agile)开发方法,例如极限编程(XP),这种开发方法采用一种增量学习及开发方法。在 XP 中有一种称为 ModelFirst 的过程,这个过程涉及到首先构建域模型作为一种机制来组织和实现用户场景。基本说来,您要构建域模型作为您要实现的用户场景的首要部分,然后在域模型之上构建一个用户界面(UI)作为用户场景实现的结果。这种方法非常适合让一个开发组一次只学到一种技术,而不是让他们同时面对很多种情况(或者让他们读很多书),这会令他们崩溃的。 

还有,对每个应用程序层重复的开发可能会包含一些适当的模式及最佳实践。如果您从应用程序的底层开始应用一些模式如数据访问对象和会话 Facades,您就不应该在您的JSP和其他视图对象中使用域逻辑。 

最后,当您开发一些简单的模块时,在开始的初期就可以对您的应用程序进行性能测试。如果直到应用程序开发的后期才进行性能测试的话,这往往会出现灾难性的后果。 

当使用 EJB 组件时,始终使用会话 Facades。 

决不要将实体 bean 直接暴露给任何用户类型。对实体 bean 只可以使用本地 EJB 接口(Local EJB interfaces)。 

当使用 EJB 组件时,使用一个会话 Facades 是一个确认无疑的最佳实践。实际上,这个通用的实践被广泛地应用到任何分布式的技术中,包括 CORBAEJB 和 DCOM。从根本上讲,您的应用程序的分布"交叉区域"越是底层化,对小块的数据由于多次重复的网络中继造成的时间消耗就越少。要达到这个目的的方法就是:创建大粒度的 Facades 对象,这个对象包含逻辑子系统,因而可以通过一个方法调用就可以完成一些有用的业务功能。这种方法不但能够降低网络开销,而且在 EJB 内部通过为整个业务功能创建一个事务环境也可以大大地减少对数据库的访问次数。 

EJB 本地接口(从 EJB 2.0 规范开始使用)为共存的 EJB 提供了性能优化方法。本地接口必须被您的应用程序显式地进行访问,这需要代码的改变和防止以后配置 EJB 时需要应用程序的改变。由于会话 Facades 和它包含的整个 EJB 对于彼此来说都应该是本地的,我们建议对会话 Facades 后面的实体 bean 使用本地接口。然而,会话 Facades 本身的实现(典型例子如无状态会话 bean)应该设计为远程接口。 

为了性能的优化,可以将一个本地接口添加到会话 Facades。这样做利用了这样一个事实:在大多数情况下(至少在 Web 应用程序中),您的 EJB 客户端和 EJB 会共同存在于同一个 Java 虚拟机(JVM)中。另外一种情况,如果会话 Facades 在本地被调用,可以使用 J2EE 应用服务器配置优化(configuration optimizations),例如 WebSphere 中的"No Local Copies"。然而,您必须注意到这些可供选择的方案会将交互方法从按值传递(pass-by-value)改变为按引用传递(pass-by-reference)。这可能会在您的代码中产生很微妙的错误。当您要利用这些方案时,您应该在一开始就考虑其可行性

如果在您的会话 Facades 中使用远程接口(而不是本地接口),您也可以将同样的会话 Facades 在 J2EE 1.4 中以兼容的方式作为 Web 服务来配置。这是因为 JSR 109J2EE 1.4 中的 Web 服务部署部分)要求使用无状态会话 bean 的远程接口作为 EJB Web 服务和 EJB 实现的接口。这样做是值得的,因为这样做可以为您的业务逻辑增加客户端类型的数量。 

你可能感兴趣的:(最重要的J2EE最佳实践)