关键词:EJB,JEEE,Spring,OO,需求至上,J2EE Without EJB
作为一个EJB的使用者,也是受害者,对EJB虽然谈不上深恶痛绝,但也达到了敬而远之的地步。必须要强调的是EJB不是J2EE,它只是J2EE很小的一部分,EJB既不是完美的也不是必须的。
我非常反对在项目中使用EJB,它给项目带来的复杂性甚至超过了项目本身。曾有人说过:你要么把事情做的尽可能简单,让人挑不出毛病;要么把事情做的尽可能复杂,让人找不出毛病。我想EJB属于后者。
下面的文章我经过了简单的整理,我想可以很充分的说明为什么要抛弃EJB。这里和大家分享。
为什么要抛弃EJB(J2EE Without EJB)
传统的J2EE架构方案得到的结果常常无法让人满意:过于复杂的应用程序,令人失望的性能,难以测试,开发和维护成本高昂.事情原本不必这样的.对于绝大多数应用程序,原本应该有更好的选择.在本书中,我们将向读者介绍一种更加简单,而又不失强大的架构方案.这种方案有作者多年的J2EE经验作为支撑,并且使用了诸如控制反转(Inversion of Control,IoC)和AOP等较新的技术.它用更加轻量级,更加灵活的基础设施取代了EJB,并因此受益良多.本书作者和其他很多人已经在很多真实应用中使用了这种架构方案,并且得到了比传统架构更理想的结果.下面,我们就来简单看看相关的主题.在后面的章节中,我们还将深入讨论这些主题.
聚光灯下的EJB
和绝大多数同行一样,当我第一次看到EJB时,它所许诺的美景令我激动不已.那时,我深信它就是企业中间件的不二法门.可是,时至今日,我的观点早已发生了变化,而促成这种变化的正是我本人和众多同行的亲身经验.时移世异,自从EJB规范成型以来,很多事情已经变了样:EJB规范中的一些部分已经过时.譬如说,J2SE 1.3引入的动态代理(dynamic proxy)就直接对EJB采用的容器代码生成机制提出了置疑.拥有动态代理之后,真的还有必要为每个EJB实现好几个源文件吗EJB和RMI之间那种传统的紧密关系也开始显得有些不合时宜,这一方面是因为web services的迅速发展,另一方面是因为人们发现:很多时候EJB只需要本地接口.在大多数时候,EJB扮演着一种重量级的对象模型,而这样的对象模型是不需要提供远程访问能力的.
EJB最善于实现业务对象分布的体系结构,然而这种体系结构究竟有多大程度的普遍性,如今看来是相当值得怀疑的.从人们使用EJB的情况也可以看出EJB的优缺点.大多数开发者和架构师仅仅使用无状态session bean(SLSB)和(如果需要异步调用的话)message-driven bean(MDB).EJB容器为支持SLSB而提供的服务相当简单,这也就是说,对于这些应用程序来说,整个EJB容器的高昂成本很难说是合理的.尽管EJB已经存在了五年之久,并且在很多J2EE项目中得到应用,但是很显然,由于它的复杂性,很多开发者仍然没有真正理解它.譬如说,我所面试过的很多开发者都无法正确地说出EJB容器如何处理异常,自然也就更不清楚容器异常处理与事务管理之间的关系了.为了解决EJB存在的问题,EJB规范也在变得日益复杂.如今,EJB规范是如此繁复冗长,几乎没有哪个开发者或者架构师有时间去通读它,更不用说是理解它了.就像应用程序一样,如果技术规范变得日益复杂,并且需要不断地加入一些权宜之计,通
常就说明存在一些根本性的问题.
EJB是如此复杂,这也就意味着使用EJB的开发效率会相对较低.有不少工具力图解决这个问题,从"企业"IDE到XDoclet和其他代码生成工具,不一而足.但这些工具只能把复杂性暂时掩盖起来,而且采用这些工具也会增加开发成本.严格的单元测试和测试驱动开发(Test Driven Development,TDD)正在日益变得流行——这也是情理之中的.人们清楚地看到:大量使用EJB的应用程序很难测试.用测试先行(test first)的方法开发EJB应用需要做很多工作,因此,应当从根本上尽量减少应用代码对EJB容器的依赖.对于EJB致力解决的中间件问题,面向方面的程序设计(Aspect Oriented Programming,AOP)的飞速发展为我们指出了一条更为强大——并且很可能更为简单——的解决之道.从某种意义上,AOP可以看作EJB核心概念的更为广泛的应用,然而AOP还远不止是EJB潜在的替代品,它还有更多的潜力可挖.在大多数时候,源代码级的元数据属性(就像.NET中所使用的那样)可以非常优雅地取代基于XML的部署描述符——从EJB 1.1起,我们就一直在跟这些冗长的XML文件周旋.EJB 3.0似乎已经开始朝着这个方向努力了,但它背负着如此沉重的历史包袱,需要走的路还很长.
经验还表明,EJB往往招致更高的开发成本,并且提供的利益也并不像它最初所鼓吹的那么丰富.在使用EJB的过程中,开发者们遇到了很多棘手的问题.根据我们的经验,EJB的失败之处主要有以下几点:
EJB并非降低复杂度所必须的,它倒是引入了很多新的复杂度.
作为一种持久化机制,entity bean的尝试是彻底失败的.
与其他J2EE技术(例如servlet)相比,使用EJB的应用程序更加缺乏不同应用服务器之间的可移植性.尽管EJB承诺提供高度可伸缩性,然而EJB系统的性能常常不够理想,而且EJB也并不是获得可伸缩性所必须的.尽管很难得到准确的统计数据,但实际经验告诉我们:有相当一部分项目正是因为过度使用EJB而不得不重新进行架构设计,甚至是彻底失败.
EJB可能让简单的事情变得困难.譬如说,Singleton设计模式(或者与之类似的创建型模式)就很难在EJB环境下实现.expert one-on-one J2EETM Development without EJB 中文版
J2EE还剩什么
也许你要问了:"没了EJB,J2EE还剩什么呢 "
答案是:还有很多很多.J2EE远不止是EJB而已.然而,很多J2EE开发者并不这样想,要是让他们看见你案头上摆着这本书,他们恐怕要颇有微词了.不过,如果平心静气地分析一下EJB做了么,J2EE又做了什么,我们就会发现:EJB其实仅仅是J2EE的一小部分而已,整个J2EE的图景比起EJB要大得多,也重要得多.
从本质上来说,J2EE就是一大堆标准化的企业级服务——例如命名和目录服务(JNDI),为异构的事务性资源提供的标准事务管理接口(JTS和JTA),连接遗留系统的标准机制(JCA),资源池,线程管理等的集合体.J2EE真正的威力在于这些服务,它对这些服务的标准化对于整个行业非常有益.另一方面,EJB使开发者能够通过一个特定的组件模型使用这些有价值的服务,它仅仅是使用这些服务的手段之一.即便没有EJB,我们照样能够使用JNDI,JTA,JCA,资源池……以及其他所有的J2EE服务.我们可以编写代码直接使用它们(这并不像乍看上去那么可怕),也可以借助久经考验的库和框架来使用它们——后者是更好的做法,因为这些库和框架不仅可以让我们摆脱使用J2EE服务的复杂度,而且也不会招致EJB所带来的复杂度.在EJB容器所提供的服务之中,只有很少的几样是EJB独有的.而且即便对于这些EJB独有的服务,也有很好的替代品.譬如说:entity bean是J2EE唯一的数据访问组件,同时也是J2EE最饱受非议的部分.有很多非J2EE的产品可以很好地取代entity bean,例如Hibernate和JDO.在某些应用中,JDBC会是更好的选择.
容器管理的事务(Container Managed Transaction,CMT):在J2EE的版图中,EJB是唯一享受声明性事务管理待遇的.这是一项极有价值的服务.不过借助AOP,我们同样可以获得声明性事务管理的能力.CMT是架设在J2EE JTA服务之上的一个薄层.要想替换应用服务器的全局事务管理机制是非常困难(而且非常不明智)的,但在这个机制上开发一个CMT的替代品可就容易多了.业务对象的线程池缓存(thread pooling):如果只提供web界面(以及通过servlet引擎支持web services客户端)的话,我们通常不需要这项服务,因为web容器已经提供了线程池缓存,没必要在业务对象层再提供一次.只有在要通过RMI/IIOP为远程客户端应用提供支持时,我们才会需要线程池缓存,此时EJB方不失为一种良好而简单的技术选择(与前一点相关)业务对象的线程管理:EJB容器提供了线程管理的能力,开发者可以将EJB看作单线程组件来实现.照我的经验,这是高估了无状态服务对象——最有用的一种EJB——的价值.说到底,EJB尽管可以把问题掩盖在EJB fa ade之下,但它终归无法回避所有与线程相关的复杂性.实际上 ,有比EJB更好的线程管理方案可供选择.
只有在提供远程调用(remoting)这件事上,EJB是标准J2EE架构中唯一的实现方式.不过,正如我们将要看到的,只有在RMI/IIOP远程调用的领域里,EJB才算得上一种出色的实现技术;对于web services远程调用,有比EJB更好的选择.越来越多的人开始认识到:EJB试图解决太多的问题,其中很多问题本不应该由它来解决的.就拿O/R映射来说吧,这是一个复杂的问题,EJB则提供了一个复杂而糟糕的解决方案(entity bean).一些核心问题在entity bean这里直接遭到了忽视,譬如"如何把带有继承体系的对象映射到关系数据库"——entity bean根本就不允许继承.要是EJB规范的设计者们能把这些问题留给在对象持久化领域更有经验的人,而不是自己硬着头皮解决,那该有多好.EJB不是J2EE的全部.即便使用没有EJB的J2EE,我们也无须重新发明轮子——我们不必重新实现J2EE已经提供的服务,只是改变使用它们的方式而已.站在十字路口的J2EE在J2EE的整个发展历程中,现在正是一个至关重要的时刻.从很多方面来说,J2EE都是一个伟大的成功:它成功地在从前没有标准的地方建立了标准;它大大提升了企业级软件的开放程度;并且它得到了整个行业和开发者的广泛认可.另一方面,我感觉J2EE在一些方面已经开始捉襟见肘.J2EE应用开发的成本通常很高.J2EE应用项目至少和从前的非J2EE项目一样容易失败——如果不是更容易失败的话.这样的失败率高得让人难以接受.在这样的失败率之下,软件开发几乎变成了碰运气.而在J2EE遭遇失败的场景中,EJB通常都扮演着重要的角色.J2EE在易用性方面存在着严重的问题.正如我在前面说过的,J2EE应用往往很复杂,而它们原本可以不必这么复杂的.对于J2EE web应用(例如Sun Java Pet Store),情况尤为明显:很多web应用都遭遇了毫无必要的过度工程.
J2EE仍然是一门相当年轻的技术,它不够完美也是情理之中的.现在,我们应该认真考量它究竟在哪些地方使用,在哪些地方不那么适用,然后才能扬长避短.由于J2EE涵盖了很多东西,这也就意味着我们需要找出J2EE中的哪些部分提供了最多的价值,哪些部分只是补充性的基础设施.J2EE社群不断地向着更简单的解决方案,更少使用EJB的方向发展.我的前一本书Expert One-on-One J2EE Design and Development(2002年)就是朝着这个方向迈出的坚实一步.请注意,致力于推广这些解决方案的并非我孤身一人.Rickard Oberg和Jon Tirsen(Nanning AOP框架的作者)等J2EE技术社群的先锋已经向人们揭示了基于AOP的解决方案是多么强大而简洁.从Core J2EE Patterns expert one-on-one J2EETM Development without EJB 中文版第二版的修订中我们不难看出,就连Sun也无法在这一波浪潮中免疫:书中已经开始提倡使用普通Java对象.J2EE和EJB的很多问题都源自它们"以规范为驱动"的本质.历史告诉我们:最成功的标准都是从实践中发展..出来的,而不是由哪个委员会创造出来的.OMG和CORBA的例子已经让我们看到了"规范先行"的危险性:OMG成立的目标就是要创造一个分布式对象的标准,在超过300家企业的参与之下,它历尽千辛万苦才制订出一个复杂的规范.和大多数委员会制订的规范一样,开发者的易用性几乎从来没有被考虑过,于是我们得到了一个极度晦涩难用的编程模型.自然,这样的规范也不可能得到广泛的接受.
在某种意义上,J2EE是现有中间件技术的发展演化,因为它所解决的很多问题正是我们在1990后期就已经耳熟能详的——那时J2EE尚处于构思阶段.譬如说,无状态session bean实际上照搬了一种久经实践检验,早已证明了自己价值的组件形式:带有声明性事务管理的服务对象.早在EJB 1.0规范问世之前,像微软事务服务器(Microsoft Transaction Servcer,MTS)之类的中间件就已经提供了此类组件.J2EE也努力有所创新,然而它的创新常常是从制订新规范开始,而不是首先在真实的应用中检验这些技术,结果这样的创新大多以失败而告终.有状态...session bean就是这样的一个例子:这是EJB引入的一种新的,尚未得到实践检验的组件形式.五年时间过去了,它仍然难堪重任:状态的复制带来了很多微妙棘手的问题,所以大多数架构师总是尽量避免使用有状态session bean.在我看来,J2EE这种"以规范为驱动"的现状很快就会发生改变,这是一件好事.我并不认为J2EE陷入了一种"无政府"的混乱状态,但我同样无法相信开发者们会不假思索地采用J2EE规范中的每一项新特性,而不去考虑其他的替代品——尤其是来自开源社群的替代品.如果希望开发者遵循J2EE规范,那么J2EE规范首先必须是一个实用,易用的规范.这里有一个关键问题:技术的最终使用者——应用开发者,负责开发项目的项目经理,以及最终使用这些应用程序的人——才是最值得关注的.然而,在应用开发第一线看来一目了然的事实,对于那些呆在委员会里制订规范的人们来说未必总是那么一目了然的.
前行的路
简单
这世界上的确有...简单的问题,软件的架构和实现也应该尽量保持简单.正如我一直提到的,J2EE项目常常遭受过度工程,这很大程度上是因为我们先入为主地认为J2EE应用需要那么复杂.但情况并非总是如此,在某些领域,J2EE架构师们常常过高地估计了需求的复杂程度,例如:数据库的分布.很多应用只需要使用一个数据库,这也就意味着它们不需要JTA,两阶段提交或者XA事务.此时,这些高级特性都会招致不必要的性能损失,并且提高系统的复杂度.多种客户端.应用程序需要一个web界面,这是理所当然的,但很多J2EE架构师却一厢情愿地认为应用程序还必须支持远程Swing客户端."J2EE应用理应能够支持多种客户端"的想法在不少人的脑子里已经根深蒂固了.(说实话,多亏一位审稿人在审阅我以前的书稿时指出,我才意识到:需要支持多种客户端的情况其实并不常见.这也促使我更多地在本书中描述自己曾经参与过的真实项目的情况,而不是沉湎于一些不切实际的想象.)按照J2EE的正统思想,我们根本不允许客户有如此简单的需求.我们这些J2EE架构师凭自己的学识就知道:客户的业务早晚会变得复杂起来,那时我们提前叫客户掏钱购买的复杂架构就能派上用场了.
这种想法有两个问题:首先,是否让系统变得如此复杂不应该由作为架构师和开发者的我们来决定,因为买单的人不是我们;其次,即便系统最终变得如此复杂,我们又怎么知道一开始将它们考虑进来就能节约成本呢 说不定,等到有需求的时候再修改架构还会更节约呢.实际上,很可能永远不会出现这样的需求;即便它们真的出现了,我们也可以逢山开路遇水架桥.譬如说,最终要访问我们的应用程序的远程客户端可能是在Windows平台上运行的C#或者VB .NET程序,要为这些程序提供远程服务,基于EJB的远程调用体系结构就不一定是最好的选择.XP(eXtreme Programming,极限编程)的核心教义之一就是:很多时候,越是节约成本,就越能开发出高质量的软件;不要试图预先解决所有能想到的问题.
我们应该尽量降低架构的复杂度,只为现实的(和合理的可预见的)需求提供支持,不要试图预先把所有的问题都考虑进去.但是,在力求简单的同时,有必要多留意架构的设计质量,以保证未来能够对其进行重构,使其能够应对更加复杂的需求.对架构的重构不像重构代码那么简单,但既然我们不希望面对新的需求时被迫修改大量代码,就必须重视架构的重构.拥有一个具有伸缩性的简单架构是至关重要的.毕竟,我们没办法裁减EJB这样复杂的架构来适应简单的需求照我的经验,使J2EE项目具备架构重构能力的关键在于:遵循良好的OO设计法则,并且始终针对接口编程,而非针对类编程.这是经典图书Design Patterns教给我们的基本常识,可惜人们常常忽视了这一点.将EJB之类的技术隐藏在普通Java对象背后.
生产率
软件的生产率是一个极其重要的问题,但J2EE的正统思想常常忽视了这一点.J2EE在提升生产率方面的记录并不漂亮.J2EE开发者常常要用大把的时间来与API和复杂的部署问题周旋,而这些时间本应该用来处理业务逻辑的.和使用CORBA的日子比起来,J2EE的生产率有所提高,但仍然不能令人满意.在这些被浪费的时间中,有很大部分都是与EJB相关的.
OO
当然了,我们要OO.不过,既然Java是一种相当好的OO语言,难道J2EE应用不是天生就面向对象的吗它们应该是,但实际上很多J2EE应用仅仅是"EJB应用"或者"J2EE应用",而不是OO应用.很多常见的J2EE实践和模式过于轻率地背弃了面向对象的原则.OO的设计比具体的技术(例如J2EE)要更加重要.我们应该尽量避免让技术上的选择(例如J2EE)妨碍我们使用真正的OO设计.让我们来看两个例子,看看很多J2EE是如何背弃了OO的原则:使用带有远程接口的EJB,以便分布业务对象.设计一个业务对象带有远程接口,具备分布能力的应用程序,这是对OO的严重破坏.出于性能的考虑,带有远程接口的组件必须提供粗粒度的接口,以避免频繁的调用.另外,它们还需要以传输对象或者值对象的形式传递输入和输出参数.的确有...一些应用必须提供分布式的业务对象,但大多数应用并不需要这样做,它们最好是让业务对象呆在自己应该在的地方.使用分布式对象的问题并非EJB一家独有的,EJB不是第一个分布式对象技术,也不会是最后一个.这个问题之所以总是和EJB联系在一起,是因为组件的分布恰好是EJB处理得比较好的几件事之一——也许好得过头了.认为持久对象不应该包含任何行为.很长时间以来,这一直是J2EE开发者不容置疑的信条,包括我本人也曾经信奉过它——自从Expert One-on-One J2EE Design and Development出版之后,我的思想发生了一些转变,这就是其中的转变之一.
实际上,J2EE开发者们之所以有这样的信念,更多的是因为entity bean在技术上的严重缺陷,而不是因为什么所谓的设计原则.仅仅暴露getter和setter(例如,通过getter和setter暴露持久化数据)的对象不是真正的对象,一个真正的对象理应把针对自己状态的行为动作封装起来.使用entity bean会促使开发者把这种缺陷看作一种规范的做法,因为entity bean中的业务逻辑很难测试,而且不可避免地与特定的持久化策略捆绑在一起.(譬如说,如果在entity bean中编写了大量业务逻辑,那么很显然,为了获得足够高的性能,唯一的办法就是使用SQL和JDBC访问关系型数据,这是一种典型的需要重构的做法.)在这里,更好的方案是使用JDO或者Hibernate之类的透明持久化技术,因为它们可以让持久对象成为真正的对象,在其中放置更多的业务逻辑,而不会使这些业务逻辑对持久化细节有过多的依赖.一旦你发现自己正在编写一个"不是真正对象" ——也就是说,只有一些用于暴露数据的方法——的对象,你就应该想想自己为什么要这样做,是否有更好的选择.遵循OO原则自有其价值.运用合理的话,OO可以带来非常高的代码复用率和非常优雅的设计.
需求至上
应用架构应该由业务需求来驱动,而不是以技术为目标,这本来应该是显而易见的事.可惜,在J2EE这里,情况似乎并非总是如此.J2EE开发者们常常凭空想出一些需求,例如:需要同时支持多个数据库,此前我们已经讨论过这个问题了;要求能够毫无成本地移植到别的应用服务器;要求能够轻松地移植到别的数据库;
支持多种客户端.这些都是潜在的...业务需求,但它们是不是真实的...需求 这就需要具体情况具体分析.使用其他技术(例如.NET)的开发者通常并不需要操心这些空想的需求,这也就是说:J2EE开发者们仅仅因为他们的技术选择——而不是客户的需求——就耗费了更多的精力.J2EE让很多在其他技术中不可能做到的事情成为了可能,这是J2EE的一大优势,但同时也是一个潜在的危险,因为它常常会让我们忘记:尽管我们可以做到所有这些事,但做每件事都是需要成本的.如果没有一个足够好的理由,我们不应该让客户承担所有这些成本.
经验过程
我的妻子是一位医生.近年来,一种名叫循证医学(Evidence-Based Medicine,EBM)的模式对医生们的工作产生了很大的影响.按照这种模式,治疗决策需要在很大程度上参考医学研究的证据.而在此之前,医生们通常都是根据患者的状况和不同的选择在以前的结果来决定哪种治疗方案的.尽管EBM对医学实践的影响未必全都是积极的,但类似这样的经验方法无疑值得软件开发者们学习.
我们的IT行业很大程度上是由时尚和激情来驱动的.我几乎每天都在听到不同的人们重复着那些他们无法验证其正确性的观点(例如"EJB应用天生就比不使用EJB的应用更具可伸缩性"),或者重复着一些根本没有真实证据能够提供支持的宗教信仰(例如".NET不是一个可靠的企业级平台").而这也就意味着人们常常是凭着一种固执在架构企业级系统.总之,他们知道什么是最佳方案——然而,并没有足够的证据能够证明这一点.对于这种情况,或许最好的办法就是"问问电脑"(这个说法来自另一位技术作家Randy Stafford).在耗费太多的钱和时间在我们所推荐的体系结构上面之前,我们应该始终"问问电脑"对这个体系结构究竟怎么想.在迭代式方法中,这种做法被总结为可执行架构(来自RUP),垂直切片(vertical slice)或者穿刺方案(来自XP).不管叫什么名字,总之是要尽快搭建一个完整的,可执行的示例应用,以尽量减少采用采用此架构的风险.在RUP这里,这一过程的目标是找出风险性最大的架构问题,以最大程度地降低风险;在XP这里,这个过程则是由核心用户故事(user story)来驱动的.
不管怎样,这一过程必须与实际需求紧密相关.如果采用敏捷开发流程,那么我们会很自然地创建一个垂直切片,所以就没有必要单独强调这一过程了.当垂直切片建立起来之后,我们就可以在它的基础上验证架构选择是否合理.其中重要的衡量标准包括:性能.这个架构能满足非功能性的需求吗 这很有可能成为J2EE应用的症结.有很多项目存在棘手的性能问题,如果没有早期的垂直切片,我们常常要到最后一分钟才能发现这些问题.采用的难度.开发者在这个架构上耗费的时间和成本是否与实现的需求成比例 用它开发出的产品的复杂度是否与需求的复杂度成比例 采用这个架构的开发过程是可重复的吗 或者需要某种魔法才能获得成功可维护性.在这个垂直切片的基础上增加新的功能困难吗 一个新手花多少时间可以理解这个应用的架构和实现,并投入有效的开发非常遗憾,很多项目并没有进行这样的风险缓解.更糟糕的是,很多广受推荐的J2EE架构实际上是由技术规范或者厂商来推动的,它们并没有在真实的生产环境中证明自己,所以它们不值得信任.
不仅不要相信这些技术架构,也不要相信我们或者别的任何人.请为你的应用搭建一个垂直切片,根据你的关键需求去考察它."问问电脑"哪个架构最适合你的需求.你的需求也许是独一无二的,但本书中介绍的架构已经在很多项目中获得了成功,并且已经证明自己能够制造出优秀的系统.所以,不妨把它作为你的第一个考察对象.
可测试性
在最近几年中,测试先行的开发(test first development)日益流行,而且通常都能收获令人满意的结果.然而,是否能够编写有效的单元测试不仅取决于开发者为此投入的时间和精力,还取决于应用程序的高层架构.在本书中,我会反复强调:应用架构理当让开发者能够轻易地编写有效的单元测试.而这正是EJB最大的缺陷之一:由于对EJB容器依赖过重,在EJB中编写的业务逻辑非常难测试.难以测试的代码通常也难以修改,难以在不同环境下复用,难以重构.可测试性是敏捷(agile)项目的基本要素.本书所介绍的J2EE架构方案很适合敏捷项目使用,我们也会常常讨论关于敏捷的话题.
轻量级框架和容器
每个应用程序都需要一些基础设施,拒绝使用EJB并不意味着拒绝EJB所采用的基础设施解决方案.我们当然不想回到1990年代后期,EJB出现之前的状况:为了开发一个复杂的Java企业级应用,人们必须亲手实现资源池缓存,线程管理,服务定位,数据访问层等等基础设施.本书将向读者展示:如何利用现有的框架提供这些基础设施服务.我们相信,对于成功的J2EE项目,这些替代EJB的基础设施是不可或缺的.因此,介绍轻量级容器的功能和用法就成为了本书的核心内容之一.回顾2003年,这样的"轻量级"容器如同雨后春笋般层出不穷,它们提供了管理业务对象和企业级服务的能力,而不必求助于沉重的EJB基础设施.这也反映出一个事实:越来越多的人正在朝着更简单,更轻量的J2EE解决方案努力.很多这样的框架——例如Spring, PicoContainer和Nanning——都来自繁荣的Java开源社群.在第5章,我会更加详细地谈论这个话题.除了开源产品之外,也有很多商业产品——例如Mind Electric公司的GLUE web services产品——在其他领域提供了比EJB更为轻量级的解决方案,例如远程调用.
Spring框架
Spring框架(http://www.springframework.org)是一个流行的开源产品,它的作用是为J2EE应用常见的问题提供简单,有效的解决方案.2003年,Expert One-on-One J2EE Design and Development一书不同寻常地以一个完整而有发展前途的应用框架作为示例,随后,从该书的示例代码中发展出了Spring项目.Spring有一个兴旺蓬勃的开发者和用户社群,并日益变得强大而可靠,质量远远超过任何我独自开发的东西.(本书的合作者Juergen Hoeller是Spring项目的领导者之一,他为项目社群的建设作出了无法估量的贡献.)在Spring项目中,基础框架的设计甚至早于Expert One-on-One J2EE Design and Development的写作,它们来自我过去几个商业项目的经验.
在构思之初,我们并没有打算让Spring成为EJB的替代品,但它确实为普通Java对象提供了强大,可测试的功能实现(例如声明性事务管理),让很多开发者能够在很多项目中避免使用EJB. Spring并不是唯一一个这样的项目.在本书中所介绍的设计,鲜有专属于Spring的.譬如说,我们建议使用AOP来解决一些常见的企业级问题,除了Spring之外还有好几个别的开源AOP框架可供选择.大量使用具有产品级质量的开源代码,这是本书最大的独特之处.绝大多数J2EE书籍都会提供一些代码示例,但很可惜,这些代码的价值非常有限,因为它们所展示的都是出于教学目的而大大简化了的问题.如果读者试图在真实应用中使用这些代码,他很快就会遇到麻烦.所以,使用一个具体的框架——即便并非所有读者都将使用这个框架——来阐释作者的观点,这种做法只适合用于介绍简单得失去真实意义的解决方案,因为惟其如此,才能把整个解决方案放进书中.
Spring为体系结构重构提供了绝佳的支持.譬如说,你可以在应用架构中加入带有本地接口的EJB,也可以给业务对象添加AOP支持,而不必修改任何一行调用代码——当然,你必须遵循基本的编程原则:针对接口编程,而不是针对类编程.
我们还应该使用EJB吗
EJB仍然有它的位置.本书介绍了一种比EJB更简单,生产率更高的架构方案,它适用于很多J2EE应用.但是,我们不会声称这种方案是J2EE的万灵药.在Expert One-on-One J2EE Design and Development一书中,我不止一次提到过"帕累托法则".这个法则也常常被称为"80/20(或者90/10)法则",也就是说:花比较少(10%—20%)的力气就可以解决大部分(80%—90%)的问题,而要解决剩下的少部分问题则需要多得多的努力.在软件架构这里,这个法则告诉我们:架构的价值在于为常见的问题找到好的解决方案,而不是一心想要解决更复杂也更罕见的问题.EJB的问题就在于它违背了"帕累托法则":为了满足少数情况下的特殊要求,它给大多数使用者强加了不必要的复杂性.比如说,也许只有10%的应用需要分布式的业务对象,然而EJB的基础结构却与对象分布紧密相关.EJB 2.1以及此前的entity bean被设计为与数据存储机制无关,但绝大多数J2EE应用都使用关系数据库,因此它们能够从entity bean的存储机制无关性中得到的利益微乎其微.("各种数据存储介质之间的可移植性"在理论上很有价值,但在实际应用中就未必如此了.而且,尽管声称提供了这种可移植性,但在对象数据库这里,entity bean的表现也无法令人满意.访问对象数据库的最佳途径仍然是使用它们自己提供的API,或者JDO之类的解决方案.)对于那些真正需要对象分布的应用,EJB仍然是上佳之选——尤其是当它们完全用Java实现,或者用IIOP作为通信协议时.不过,这种应用比人们通常想象的要罕见得多.对于需要大量使用异步消息的应用,EJB也是不错的解决方案,因为用message driven bean处理异步消息非常有效——而且相当简单.
译者注:时至今日,EJB在处理异步消息方面的优势也受到了来自轻量级阵营的挑战.Apache组织属下的Jakarta CommonsMessenger是一个轻量级的,基于POJO的JMS消息框架,使用这个框架可以——如MDB那般——轻松地发送和消费JMS消息,而它所需的运行环境仅仅是J2EE web容器.目前Commons Messenger项目仍处于沙箱(sandbox)阶段,但它(或者类似的其他产品)进入真实应用仅仅是个时间问题.读者可以在http://jakarta.apache.org/commons/sandbox/messenger/H找到这个项目的相关信息.expert one-on-one J2EETM Development without EJB 中文版
EJB能够真正为项目提升价值的典型例子或许就是金融中间件应用.金融应用中的处理过程常常需要耗费大量的时间和计算能力,比起业务处理本身的开销,远程调用的开销常常倒是可以忽略不计的了.而且,金融中间件也通常是面向消息的,很适合使用MDB.我相信,这样的应用就是那真正复杂的10%.当然,也许还有强大的政治因素....(而非技术因素)促使人们使用EJB,这已经不在本书的讨论范围之内了.照我的经验,赢得政治斗争通常比赢得技术斗争要困难得多.我相信EJB是一种正在逐渐衰退的技术,不出三年它就会进入弥留阶段,尽管EJB 3.0竭力改进也于事无补.但本书关注的是帮助你马上着手搭建企业级应用,所以如果EJB能够很好地应对你眼下的需求,我就会建议你使用EJB——但只是当下暂且使用而已.
小结
在本章中,我们大致浏览了一下本书后面章节将要详细讨论的各个主题.从J2EE诞生之初,EJB就一直被视为J2EE平台的核心,但我们认为这是一个误解.EJB有它的位置,但对于大多数应用来说,不使用EJB会是更好的选择.J2EE远不止是EJB,EJB只是使用J2EE服务的途径之一.因此,拒绝EJB决不意味着放弃J2EE.我们相信轻量级容器(例如Spring框架)能够更好地组织应用代码,更好地使用J2EE提供的服务.在本书中,我们将详细介绍这种架构方案.我们相信业务需求和基本的OO常识——而非具体的实现技术——应该是项目和架构的驱动力.
译者注:马基雅维利(Niccolo Machiavelli,1469~1527)是文艺复兴时期的意大利政治思想家,历史学家和军事理论家.他的政治科学名著《君主论》以不受感情和伦理约束的冷静客观态度指出了君主的治国之道,因此常常被认为是"为达目的不择手段"的政治诡道——也即所谓"马基雅维利主义"——的代表.