第一部分 整体介绍
第1章 EJB概述
Sun公司正式推出了EJB(Enterprise Java Bean)规范之后,在众多的公司和开发人员中引起了很大的反响,许多公司都已经推出了或正打算推出EJB产品。EJB已经成为Java技术的研究重点,它是由Sun牵头、IBM等公司共同参与制定的分布式计算解决方案。其目的主要是为了解决重用组件、商务规则的隐藏等问题,提高软件开发的可伸缩性,满足Internet的分布式计算需要。
1.1 EJB的基本概念
EJB是用于开发和部署多层结构的、分布式的、面向对象的Java应用系统的跨平台的构件体系结构。采用EJB可以使开发商业应用系统变得容易,应用系统可以在一个支持EJB的环境中开发,开发完之后,部署在其他的环境中。随着需求的改变,应用系统可以不加修改地迁移到其他功能更强、更复杂的服务器上。
1.1.1 为什么要使用EJB
如今开发电子商务平台已大量使用组件技术,这是因为组件技术提供了服务器上的自治、企业级和分布式功能,并帮助开发者在不同颗粒度级别上定义和封装系统功能。通过采纳组件技术,比传统程序更易于为日后的需求进行维护、支持和修改。在金融行业中,利用以服务器为中心方式的优势在于,可以定义商业过程,将其作为一组软件组件编写,然后通过多种渠道传递。一旦使金融系统变为由单独的自治组件(而非单一庞大程序)组成,灵活性就随之而来。
EJB的组件结构是以作为可复用的服务器端组件而设计的,它使企业能够建立可升级、安全可靠、可运行于多重平台且以商务为重点的应用程序。EJB可以让企业开发人员只集中于开发商务逻辑,而不用花费精力去处理分布式服务器端系统所带来的底层问题,从而使开发人员可以快速开发大规模的企业应用。
使用EJB技术可以使我们获得以下收益。
1.生产效率
通过使用EJB,企业开发人员将会进一步提高生产效率。他们不仅能够获得在Java平台上的开发成果,而且能够将注意力集中于商务逻辑,从而使效率倍增。
2.业内支持
试图建立EJB系统的客户会获得一系列可供选择的解决方案。对于现有的应用系统,许多EJB产品的供应商(例如IBM和BEA)都提供了完善的升级手段,将系统升级到EJB模式。EJB技术已经被越来越多的公司所接受、支持和应用。
3.结构独立
EJB技术支持“即插即用”的企业级特性。它将开发人员和底层中间件相隔离,开发人员看到的仅仅是J2EE平台,这样使得EJB服务器厂商在不干扰用户的EJB应用程序的前提下,有机会改进中间件层。
4.跨平台、跨厂商
通过对Java平台的支持,EJB技术将“仅写一次,随处运行”的概念提高到了一个新的水平。它可以保证一个EJB应用程序可运行于任何服务器,只要这个服务器能够真正提供EJB APIs。
5.EJB组件能提供真正的可重用框架
每一个jar包代表一个EJB组件,一个系统可以由多个可重用的EJB组件构成,例如:树形结构EJB组件;自增序号EJB组件;用户资料EJB组件等,这样的EJB组件可以像积木一样搭配在大部分应用系统中,提高了系统的开发效率,保证了开发质量。
6.EJB提供了事务机制
事务机制对于一些关键事务是很重要的,例如ATM机提款,提款有多个动作:修改数据库及数钱等,如果这其中有任何一个环节出错,那么,其他已经实现的操作必须还原,否则,就会出现提款人没有拿到钱,但是卡上已经扣款等不可思议的事情。
1.2 EJB的体系结构
1.2.1 EJB的基础结构
容器和服务提供者实现了EJB的基础构造,这些基础构造处理了EJB的分布式、事务管理和安全性。EJB规范定义了基础构造和Java API的各种要求,而没有去指定用什么技术、平台和协议来实现他们。
Enterprise Beans,container,server说明了EJB基础结构的要求,EJB基础结构必须提供客户和Enterprise Beans通信的通道。虽然这不是EJB规范所定义的,但保证通道的安全也是很重要的,特别是当客户通过Internet访问远程的Enterprise Beans时,尤为重要。EJB基础结构也必须能够加强Enterprise Beans的访问控制。如图1-1所示为EJB的结构。
图1-1 EJB的结构
EJB的上层分布式应用程序是基于对象组件模型的,低层的事务服务应用了API技术。EJB技术简化了用Java语言编写的企业应用系统的开发、配置和执行,其体系结构的规范由Sun Microsystems公司制定。Inprise的EJB容器是基于1.1版的规范。EJB技术定义了一组可重用的组件:Enterprise Beans。可以利用这些组件,像搭积木一样建立分布式应用程序。当把代码写好后,这些组件就被组合到特定的文件中去,每个文件有一个或多个Enterprise Beans,再加上一些配置参数。最后,这些Enterprise Beans被配置到一个装有EJB容器的平台上。客户能够通过这些Beans的home接口,定位到某个Beans,并产生该Beans的一个实例。这样,客户就能够调用Beans的应用方法和远程接口。
建立一个基于对象的、多层的、分布式的系统有三种途径:无状态服务途径、基于会话途径和持续对象途径。
无状态的服务是通过对象的操作提供一种功能的函数,但是不保持会话的状态。当一个客户使用无状态的对象时,客户不能够提供上一次操作的信息。
基于会话的设计产生了一个中间层的对象,称为一个会话(Session),这个会话可以看成是客户的代理。典型的会话的生命是由客户和所在的服务程序决定的。客户如果完成了会话就可以将对象移走,如果服务终止了,会话对象就会超时,会话对象就会变得无效。
持续对象设计模式绑定了存在于数据库中的一块数据,提供了操作这块数据的一些操作。持续对象是由多个客户共享的,其生命期是由存储数据的数据库所决定的。
EJB规范中将这些分别称作:stateless Session,stateful Session和Entiry Beans。Session Beans模式就是基于会话的设计模式,Entiry Beans模式就是持续对象设计模式,每种模式都定义了一些接口和命名约定。
1.2.2 EJB的体系结构
EJB服务器作为容器和低层平台的桥梁,管理着EJB容器和函数,它向EJB容器提供了访问系统服务的能力。例如:数据库的管理和事务的管理。所有的EJB实例都运行在EJB容器中。容器提供了系统级的服务,控制了EJB的生命周期。EJB分布式应用程序是基于对象组件模型的,低层的事务服务使用了API技术。EJB技术简化了用JAVA语言编写的企业应用系统的开发、配置。客户能够通过这些Beans的home接口,定位到某个Beans,并产生该Beans的一个实例。这样,客户就能够调用Beans的应用方法和远程接口。EJB中有一些易于使用的管理工具,如:Security——配置描述器(The Deployment Descriptor)定义了客户能够访问的不同的应用函数。容器通过只允许授权的客户访问这些函数以达到这个效果。Remote Connectivity——容器为远程链接管理着低层的通信issues,而且对Enterprise Beas的开发者和客户都隐藏了通信细节。EJB的开发者在编写应用方法的时候,就像是在调用本地的平台一样。客户也不清楚他们调用的方法可能是在远程被处理的。Life Cycle managment——客户简单地创建一个Enterprise Beans的实例,并通常取消一个实例。而容器管理着Enterprise Beans的实例,使Enterprise Beans实现最大的效能和内存利用率。容器能够激活和使Enterprise Beans失效,保持众多客户共享的实例池。Transaction management——配置描述器定义了Enterprise Beans的事务处理的需求,容器管理着那些管理分布式事务处理的复杂的issues。这些事务可能要在不同的平台之间更新数据库,容器使这些事务之间互相独立,互不干扰,保证所有的更新数据库都是成功发生的,否则,就回滚到事务处理之前的状态。
EJB组件是基于分布式事务处理的企业级应用程序的组件。所有的EJB都有如下特点:
(1)包含了处理企业数据的应用逻辑。
(2)定义了EJB的客户界面。该界面不受容器和服务器的影响。于是,当一个EJB被集合到一个应用程序中去时,不用更改代码和重新编译。
(3)Enterprise Beans能够被定制。
(4)各种系统级的服务,例如安全和事务处理的特性,都不是属于Enterprise Beans Class的,而是由配置和组装应用程序的工具来实现。
有两种类型的EJB:Session Beans和Entity Beans。Session Beans是一种作为单用户执行的对象。作为对远程任务请求的响应,容器产生一个Session Beans的实例。一个Session Beans有一个用户,某种程度上,一个Session Bean对于服务器来说就代表了它的那个用户。Session Beans也可以用于事务,能够更新共享的数据,但不直接描绘这些共享数据。Session Beans的生命周期相对来说是比较短的。典型地,只有当用户保持会话时,Session Beans才是活着的,一旦用户退出Session Beans就不再与用户相联系。Session Beans被看成是瞬时的,因为如果容器崩溃了,那么,用户必须重新建立一个新的Session对象来继续会话。
Session Bean声明了与用户的互操作或者会话,也就是说,在客户会话期间,Session Bean通过方法的调用,掌握用户的信息。一个具有状态的Session Bean称为有状态的Session Bean。当用户终止与Session Beans互操作时,会话终止了,而且,Bean也不再拥有状态值。Session Bean也可能是一个无状态的session Bean,无状态的Session Beans并不掌握其客户的信息或状态。用户能够通过调用Beans的方法来完成一些操作。但是,Beans只是在方法调用时才知道用户的参数变量,当方法调用完成后,Beans并不继续保持这些参数变量。所有无状态的Session Beans的实例都是相同的,除非正在方法调用期间。这样,无状态的Session Beans就能够支持多个用户。容器能够声明一个无状态的Session Beans,能够将任何Session Beans指定给任何用户。
Entity Beans为数据库中的数据提供了一种对象视图,例如:一个Entity Beans能够模拟数据库表中一行相关的数据。多个client能够共享访问同一个Entity Beans。多个client也能够同时访问同一个Entity Beans。Entity Beans通过事务的上下文来访问或更新下层的数据,保证了数据的完整性。Entity Beans能存活相对较长的时间,并且状态是持续的,只要数据库中的数据存在,Entity Beans就一直存活或者服务进程来。即使EJB容器崩溃了,Entity Beans也是存活的。Entity Beans生命周期能够被容器或者Beans自己管理。如果由Beans自己管理,就必须写Entity Beans的代码,包括访问数据库的调用。
1.3 JavaBean与EJB的不同
JavaBean和EJB有一些相同之处,都是用一组特性创建的,以执行其特定任务的对象或组件。它们还具有从当前所驻留服务器上的容器获得其他特性的能力,这使得Bean的行为可以根据特定任务和所在环境的不同而有所不同,进而开辟了巨大商机。由于JavaBean是与平台无关的,所以对于将来的解决方案,供应商可以很容易地向不同用户推出其客户机方的JavaBean,而不必创建或维护不同的版本。这些JavaBean可以与执行商业功能(例如订购、信用卡处理、电子汇款、存货分配、运输等)的EJB配合使用,这正是组件代理(WebSphere Application Server企业版)设计提供的巨大潜力。
JavaBean是一种组件,它在内部有接口或有与其相关的属性,以便不同人在不同时间开发的Bean可以询问和集成。可以先构建一个Bean,然后在以后构造时将它与其他Bean绑定。这种过程提供了先构建,然后重复使用的方法,这就是组件的概念。可以将这种单一应用程序部署成独立程序、ActiveX组件或嵌入在浏览器中。JavaBean因有外部接口(即属性接口)而与纯对象不同。这种接口允许工具读取组件要执行的功能,将其与其他Bean挂钩,以及将其插入其他环境。JavaBean设计成对单一进程而言是本地的,在运行时通常是可视的,这种可视组件可能是按钮、列表框、图形或图表——但这不是必需的。
Server Bean或EJB是部署在服务器上的可执行组件或商业对象。有一个协议允许对其进行远程访问或在特定服务器上安装或部署它们,有一系列机制允许它们将服务安全性、事务行为、并发性(由多个客户机同时访问的能力)和持久性(其状态可以保存多久)的主要方面授权给EJB服务器上其所在的容器。当安装在容器中时,它们获得各自的行为,该行为提供不同质量的服务,因此,选择正确的EJB服务器至关重要,这正是IBM WebSphere企业版的优势所在。
EJB是运行在服务器上、并由客户机调用的非可视远程对象,可通过多个非可视JavaBean构建EJB。它有一个部署描述符,其目的与JavaBean属性相同:它是以后可由工具读取的Bean的描述。EJB还独立于平台,一旦编写好,可以在任何支持Java的平台(包括客户机和服务器)上使用。因为EJB由诸如IBM VisualAge for Java这样的工具集生成,所以,它是基于服务器的对象,并用于远程调用。它们可以安装在EJB服务器上,并像调用其他CORBA远程对象那样获得进行调用的远程接口。
ActiveX对象:可以将JavaBean部署成ActiveX对象,虽然EJB的代理也可以这样做,但是,由于ActiveX运行在桌面上,所以,EJB本身不能成为ActiveX对象。若要在与平台相关的、仅Windows平台上做到这一点,开发人员可以将JavaBean换成ActiveX组件。EJB的主要好处在于:构建Bean时,Bean开发人员可以规定需要什么类型的行为,而不必规定如何去做。开发分为两部分:第一步,程序员开发Bean,然后验证。它可与构建工具一起工作,并包括标识所需服务质量行为种类的部署描述符。第二步,另一个程序员可以采用这个Bean,并使用读取EJB部署描述符的部署工具,然后,将该Bean安装到Enterprise Java Server上的容器中。在第二步中,部署工具采取一些操作——这可能意味着生成如状态保存代码、放入事务挂钩,或执行安全性检查这样的代码。所有这些操作由部署工具生成,Bean开发人员和部署人员可以是不同的人。
可以通过使用部署工具,将任何独立于平台的JavaBean改写成具有可靠服务质量、特定于平台的EJB,以满足现有商业系统和应用程序的特定需求,这就是EJB服务器对集成系统、网络和体系结构如此重要的原因所在。Bean的全部意义不只是其现有能力,更在于其可以为商业提供有竞争力的潜在能力。IT设计师和应用开发人员可以将精力完全集中在商业逻辑,而将如事务、持久性和安全性的底层工作留给服务器。
1.4 备受争议的EJB
EJB是一种企业应用技术,旨在建立一个企业应用开发框架,但从其诞生之日起,质疑之声就一直不断,复杂、难以使用、性能低下、烦琐等。在Java发展史上,曾有过很多重要的时刻。如在20世纪末,也就是在1998年,JSP和EJB的诞生就是一个不同寻常的时刻。JSP诞生后,立刻引起了很多开发人员的注意,并很快成为Web开发的主流。而几乎和它同时诞生的EJB 1.0却一直备受冷落。在EJB 1.0诞生后的几年,Sun又推出了EJB 2.0规范,不过它的命运也与EJB 1.0相同,还是没有翻身。这其中最大的原因,我想是因为Sun没有兑现它的承诺而造成的。
Sun在发布J2EE相关规范和产品时承诺,J2EE将会使开发变得更容易,从而会显著降低开发成本。但在J2EE发布时,满心欢喜的人们却发现,被认为是J2EE中最有价值的组成部分——EJB却是如此复杂。在编写EJB时需要进行大量的配置,而且还需要实现一大堆的接口。这不但没有降低开发难度,反而成为很多开发人员的噩梦。在EJB 2.x刚出来的几年,国内有很多程序员盲目跟风,但当时,他们中的大多数都只是停留在EJB的“名词”阶段。而当他们开始熟悉并使用EJB时,却发现并不是像他们想得那样美妙。
实体Bean在EJB 2.0后就成为EJB最重要的一部分,但是它的概念从来就没清楚过。如Sun建议将业务逻辑代码放到会话Bean中,也就是说,前端应该直接访问会话Bean。而作为对数据直接封装的实体Bean却提供了远程接口,这也就意味着前端也可以直接访问实体Bean,这就与多程序应用结构不太符合。再如,实体Bean既然是对数据的原始封装,那为什么要提供事务、安全这些业务逻辑层的功能呢?更不可思议的是,实体Bean既然提供了本地接口,那又为什么不通过本地接口,而要通过JNDI查找呢?这些概念上的混淆使得EJB更加难以使用。
EJB技术正在像其他辉煌过的技术一样走到一个关口。2000年以前,这项技术充满了传奇色彩,被大批企业不假思索地接受。然而,理想毕竟是理想,经过了几年的发展,今天这项技术却正在被怀疑或者说至少让技术人员犹豫不决。现实是:J2EE的对手出来了,.NET似乎又有着后发的技术优势。大部分探讨和争论已经开始转向这两个体系结构的对比。Java阵营内部同样发出了怀疑的声音,最直接的就是对EJB的攻击,因为人们发现这项技术所做的承诺似乎都走向了相反的方向。EJB不成熟,但不等于可以轻易被否定——是EJB使得很多普通的程序员能够介入原来贵族似的组件开发,甚至是在Windows上面开发UNIX上的组件,EJB的历史问题大多数在于将这种技术错误地滥用:一个浏览人数少得可怜的广告浏览程序也要用组件,为一个只想简单算出库存的客户设计了所谓N年后才需要的扩展性。同样,现实中在这一技术擅长的领域,至少目前还无法找到更强大的竞争者。
J2EE是第一个为业界所广为接受的完整的企业应用框架,而EJB在其中扮演着重要角色。在J2EE框架的支持下,运行在EJB容器中的EJB,完全符合企业应用关于分布、移植、安全和交易的要求。这对于企业应用的开发者来说,意义非同寻常。首先,大家可以在一个公共的平台技术上构建自己的企业应用,不必绞尽脑汁“发明”自己的“轮子”,从而节省大量无谓的、重复性的技术和时间投入;其次,一个公开的平台,让大量的企业应用开发者有了共同语言,可以相互交流平台的使用经验和教训。这样,随着平台之上企业应用的不断增加,平台的优劣得失一览无余,有利于平台的改进和发展。
1.5 EJB 3.0规范全新体验
最近,期待已久的EJB 3.0规范发布了初稿,两个重要的变更分别是:使用了Java 5中的程序注释工具和基于Hibernate的O/R映射模型。
1.5.1 EJB 3.0中两个重要的变更
EJB 3.0中两个重要的变更分别是:使用了Java5中的程序注释工具和基于Hibernate的O/R映射模型。
1.Java 5中的程序注释工具
Java 5(以前叫J2SE 1.5或Tiger)中加入了一种新的程序注释工具。通过该工具用户可以自定义注释标记,通过这些自定义标记来注释字段、方法、类等。这些注释并不会影响程序的语义,但是可以通过工具(编译时或运行时)来解释这些标记,并产生附加的内容(比如部署描述文件),或者强制某些必须的运行时行为(比如EJB组件的状态特性)。注释的解析可以使用源文件的解析(比如编译器或IDE工具)或者使用Java 5中的APIs反射机制,注释只能被定义在源代码层。由于所有被提交到EJB 3.0草案中的注释标记都有一个运行时的RetentionPolicy,因此,会增加类文件占用的存储空间,但这却给容器制造商和工具制造商带来了方便。在已经提交的EJB 3.0规范中主要涉及两个方面的改变:
(1)一套以注释为基础的EJB编程模型,再加上EJB 2.1中定义的通过部署描述符和几个接口定义的应用程序行为。
(2)新的实体Bean持久化模型,EJBQL也有许多重要的改变。
还有一些有关上述的提议,比如:一个新的客户端编程模型。业务接口的使用,以及实体Bean的生命周期。请注意,EJB 2.1编程模型(包括部署描述符和home/remote接口)仍然是有效的。新的简化模型并没有完全取代EJB 2.1模型。
EJB规范组织一个重要的目标是减轻原始代码的数量,为此他们给出了一个完美而简洁的办法。在EJB 3.0里,任何类型的企业级Bean只是一个加了适当注释的简单Java对象(POJO)。注释可以用于定义Bean的业务接口、O/R映射信息和资源引用信息,效果与在EJB 2.1中定义部署描述符和接口是一样的。在EJB 3.0中,部署描述符不再是必须的了;home接口也没有了,也不必实现业务接口(容器可以完成这些事情)。比如,可以使用@Stateless注释标记类把Java类声明为一个无状态会话Bean。对于有状态会话Bean来说,@Remove注释可以用来标记一个特定的方法,通过这个注释来说明在调用这个方法之后,Bean的实例将被清除掉。为了减少描述组件的说明信息,规范组织还采纳了由异常进行配置(configuration-by-exception)的手段,意思是可以为所有的注释提供一个明确的默认值,这样,多数常规信息就可以据此推断得出。
2.新的持久化模型
新的实体Bean也是一个加了注释的简单Java对象(POJO)。一旦被EntityManager访问,它就成为了一个持久化对象,并且成为持久化上下文(context)的一部分。一个持久化上下文与一个事务上下文是松耦合的,严格地讲,它隐式地与一个事务会话共存。
实体关系与O/R映射也是通过注释来定义的,并提供几种不同的数据库规范操作,在EJB 2.1中,这些是需要通过开发人员自己的设计模式或者其他技术来完成的(比如,自增长主键策略)。
EJB 3.0必须实现的重要目标之一是,要使之成为更加有用和更易于使用的开发工具。Sun公司的Linda DeMichiel认识到,要成功实现这一目标,EJB 3.0必须基于开发人员今天正在使用的现有库;否则,它将会导致一种困难的升级操作,并且,可能不会引起足够的重视。因此,来自Oracle,JBoss,Apache,BEA,Novell, Google的成员和其他方面的专家都被邀请参与制订这一规范。这个小组的目标是,生产一种规范,能够使得EJB更易于开发,并且,还要创建一种便于开发人员能够容易地实现升级的持久性存储标准。当这个小组开始开发EJB 3.0规范时,他们很快认识到,其中很多特征应该在功能上与所有的主要供应商和库保持一致。
(1)EntityManager
EntityManager负责处理一个事务,在JDO中,它被称作持久性存储管理器,而在Hibernate中称它为一个会话。在GlassFish工程中,EntityManager被这样描述:
其实,一个EntityManager实例与一个持久性存储上下文相关联。一个持久性存储上下文是一组实体实例,其中的任何一个持久性实体都是唯一的一个实体实例。在该持久性存储上下文中,实体实例及其生命周期都是可被管理的。EntityManager定义了用于与持久性存储上下文进行交互的方法。EntityManager API用于创建和删除持久性实体实例,通过其主键查找实体和查询实体。
这个可由一个给定的EntityManager实例管理的实体集合是通过一个持久性存储单元进行定义的。一个持久性存储单元定义了所有类的集合,这些类是相联系的或由应用程序加以分组,并且,它们必须共存于其到单个数据库的映射中。
(2)命名查询
一个命名查询是一个预定义的查询,它被赋予一个名字,这样,就可以在以后通过该名字加以存取。用数据库术语来说,命名查询被称作存储过程。当结合本机查询时(见下一节),数据库查询应该是非常轻松的。
(3)本机查询
本机查询允许直接从EJB中全面使用SQL语言,而不是使用具有很多限制性的实体查询语言。现在,我们有可能直接在数据库上调用count()、max()和其他功能而不必费其他周折。
(4)回调监听器
回调监听器是一种事件监听器,或用数据库术语来说,是一种触发器。它们支持当一个事件发生时进行代码调用。
(5)脱离/重新依附对象
能够脱离开一个EntityManager的控制范围而又能够重新返回而被持续化存储,这在EJB 3.0版本之前是无法实现的。以前,为了实现这一目的,必须把来自于一个对象的值复制到一个POJO(普通Java对象)中,然后再被往回复制。在EJB 3.0之前,总是使用值、对象,并且把来自于EJB的值复制到一个POJO中;然后,在前端使用该对象。如果该POJO中的一个值被改变,它将不得不被“推回”到该EJB;然后,该值被复制回来。如今,这种“混乱”状态已经不复存在。一个对象甚至能够完全离开JVM,并且在以后某个时期返回,被重新依附,这种改变所带来的效率是不能被低估的。
值得注意的是,企业Java Bean现在被称为POJO。随着注解技术的出现,Java Bean不再需要接口、home和描述符支持文件。仅这个特征就为EJB 3.0赢得了大批开发团队的青睐。既然企业对象不再被锁定到应用程序服务器内,那么,我们就不再需要把它们复制进和复制出POJO,这样就不必把应用程序服务器后端和前端区别得那么严格,从而使开发人员能够更容易地显示和编辑存储于EJB中的数据。
1.5.2 Spring与EJB 3.0的比较
Spring框架虽然很流行,但并不是一个标准的开源框架,它主要由interface21 inc开发和控制。Spring框架结构是基于依赖注入(Dependency Injection(DI))的设计模式,可以独立地在现有的应用服务器上运行,而且大量地使用了XML配置文件。
EJB 3.0是由java community process(jcp)制订的标准框架,为所有主要的J2EE厂商支持。JBoss已经提供了试用版EJB 3.0标准的开源或商业性质实现。EJB 3.0充分利用了java的注释。
这两个框架结构有一个共同的核心设计理念:将中间件服务传递给耦合松散的pojos(plain old java objects,简单洁净java对象)。因此,开发者可专注于业务逻辑和脱离框架的POJO单元测试。除此之外,由于POJO并不需要继承框架的类或实现其接口,开发者能够极其灵活地搭建继承结构和建造应用。
1.厂商无关性
开发者选择Java平台其中最引人注目的理由之一是厂商无关性。EJB 3.0正是一套设计为厂商无关的开放性标准,它被所有企业java社团里开源或商业性质厂商所开发和支持,并将开发者与应用服务器实现完全隔离。例如,JBoss的EJB 3.0实现基于hibernate,Oracle的toplink,但是开发者并不需要学习hibernate或toplink的具体API来使应用可在JBoss或Oracle上运行。厂商无关性使EJB 3.0与现今其他POJO中间件框架区别开来。
另一方面,Spring一直以来都是非标准的技术,在未来可预知的一段时间内,这种情况将持续下去。虽然可以在任何应用服务器上使用Spring框架,但Spring应用会被锁入在Spring本身或选择整合进Spring的具体服务中。
Spring框架是一个开源项目,但同时它有一个XML格式的配置文件和编程接口。当然,任何一个非标准的产品都会有这种锁入(lock-in)的情况,并不是Spring特有的,但Spring应用的长期生存能力仍然还得依赖于该配置文件和编程接口(或者是interface21公司,它雇佣了大部分Spring核心开发人员)。除此之外,假如用到任何一个具体的Spring服务,例如,Spring事务管理器或Spring mvc,那么就会被锁入到这些API里。
Spring的应用对终端用户是不可知的,例如,对数据持久服务。Spring框架兼容不同的DAO和JDBC的模板帮助类,如Hibernate,ibatis和jdo。所以,假如需要为Spring应用切换在数据持久化服务(例如从JBDC到Hibernate),则需要修改代码以适合新的模板帮助类。
2.服务整合
从一个很高的角度上看,Spring框架处于应用服务器和服务库的上方。服务整合的代码(如数据访问模板和帮助类)属于框架,并暴露于应用开发者。相反,EJB 3.0框架与应用服务器则高度整合,并且服务整合代码也包装在一个标准接口后面。
因此,实现EJB 3.0的厂商可以大大优化整体性能和提升开发者的体验。例如,在JBoss EJB 3.0的实现中,当在用Entity Manager持久化一个Entity Bean时,后台的Hibernate会话事务已经自动地绑定到调用方法的jta的事务上,在jta事务提交的同时,Hibernate会话事务也提交了。甚至可以使用一个简单的@persistencecontext注释(稍候例子演示),将EntityManager及其后台的Hibernate事务绑定到一个Stateful Session Bean的应用事务中。在一个会话中应用事务横跨多个线程,这在事务性网页应用很有用,例如,多页面的购物车。
由于高度整合的EJB 3.0的框架使简单、集成的编程接口成为可能,Oracle EJB 3.0框架及其后台的toplink持久化服务也可以同样程度地整合。
另一个EJB 3.0整合服务的例子是集群支持。假如在一个服务器集群上部署了一个EJB 3.0的应用,所有容错(fail-over)、负载均衡、分布式缓冲和状态复制都已经自动为应用所获得。而后台的集群支持则被隐藏在EJB 3.0的框架后,对EJB 3.0开发者来说,这些都是完全透明不可见的。
在Spring里,很难优化框架和服务之间的通信。例如,为了使用Spring里的声明事务服务来管理hibernate事务,必须显式地在XML文件中配置Spring Transaction Manager和Hibernate Session Factory对象。Spring必须显式地管理横跨多个http请求的事务。除此之外,没有其他方法能够均衡Spring应用里的集群。
3.服务组合的弹性
由于Spring的服务整合代码作为编程接口的一部分暴露在外,应用开发者有按自己需求装配服务的弹性,这个特点使用户能够组合自己的轻量级应用服务器。Spring的一个普遍用法就是将Tomcat和Hibernate组合在一起支持数据库驱动的web应用,而EJB 3.0应用服务器不提供这种根据需求任意挑拣服务的弹性空间。大多数时间得到的只是一系列包装好的特性,其中一些可能根本就不需要。但是,如果应用服务器像JBoss一样提供一个模块性的内部设计,那么就可以只取其中一部分,而把不必要的部分剥去。在任何情况,去自定义一个功能强大的应用服务器那是没有什么价值的。当然,假如应用已经超过单个点,那么你应该加入常用服务器上的服务,例如,资源池(resource pooling)、消息队列(message queuing)和集群(clustering)。就总体资源消耗而言,Spring解决方法和其他EJB 3.0解决方法一样是重量级的。
在Spring框架里,具有弹性的服务装配使得将虚拟对象而不是真正的业务对象绑定到应用中做脱离容器的单元测试更简单。在EJB 3.0应用中,大多数组件都是简单POJO,可以很容易地在容器外被测试。但是,对于与容器服务相关的对象(例如持久化实体管理器Entity Manager),建议用容器内测试,因为这样比虚拟对象测试方法更简单、强壮及准确。
4.xml vs.注解
从应用开发者的观点来看,Spring的编程开发接口主要基于xml配置文件,而EJB 3.0则广泛地应用Java注解。xml可以表达复杂的关系,但是它太冗长且不够健壮;注解简单明了,但是很难在其中表达复杂或继承性的关系。
Spring选择xml或EJB 3.0选择注解,都是由他们两者框架后的体系结构决定的。因为注解只能容纳很少的配置信息,只有整合前的框架(重头戏都在框架里)才可以把广泛地使用注解作为配置选择。正如我们所讨论过的,EJB 3.0刚好符合这个要求,而Spring作为一个普通的di框架无法满足该要求。当然,EJB 3.0和Spring也相互取长补短,在某种程度上他们都支持xml和注解。例如,在EJB 3.0中,xml配置文件作为一个可选的重载机制来改变注解的默认行为,注解也可以配置一些Spring服务。
5.声明性服务
Spring和EJB 3.0都将运行时服务(例如事务、安全、日志和配置服务)绑定到应用,因为这些服务于应用的业务逻辑是没有直接联系的,他们只是由应用本身管理。换句话说,这些服务在运行时由容器透明地应用到应用中。开发者或是管理者通过配置容器,准确地告诉它什么时候、怎样应用这些服务。
EJB 3.0运用java注解来配置声明性服务,而Spring使用xml配置文件。在大多数情况下,EJB 3.0注解方式对于这种服务更简单明了。
6.依赖注入(Dependency Injection, DI)
中间件容器的关键好处之一就是它可以让开发者建造一个关系耦合松散的应用,服务端客户只需知道服务的接口。容器依据具体的实现实例化服务对象,使他们为客户端所用。在不改变接口和客户端代码的情况下,使得容器可以在多种服务之间很容易地实现切换。
依赖注入的模式是实现耦合松散应用的方法之一。它简单易用,比其他方法更加明了,比如通过JNDI依赖性查询或容器回调。使用DI,框架就像一个对象工厂,它创建服务对象,然后按照运行时配置将这些服务对象注入到应用的POJO里。从应用开发者的角度来看,客户端POJO在被使用时可自动获得正确的服务对象。
Spring和EJB 3.0都提供广泛的DI模式支持,但是他们之间仍存在很大的不同之处。Spring支持一般意义上且复杂的DI API,其基于xml配置文件,而EJB 3.0支持大多数普通服务对象(如EJB及context对象)的注入和任何简单注解的JDNI。
EJB 3.0注解简单易用。EJB 3.0标准通过注解可以注入服务器资源,但是它也支持用户定义的应用POJO之间的相互注入。虽然,Spring里基于xml的依赖注入语法复杂,但却功能强大。可以将任何POJO注入到另一个POJO,包括用户在应用定义的POJO。假如想在EJB 3.0应用中用Spring的di功能,则可以通过JNDI把一个Spring Bean factory注入到EJB。在一些EJB 3.0的应用服务器里,厂商可能会额外定义非标准的POJO注入api,一个很好的例子就是JBoss Microcontainer,它比Spring更一般化,因为它处理Aspect-Oriented Programming(AOP)的依赖。
Spring和EJB 3.0虽然都是为了向企业服务提供耦合松散的POJO,都大量地使用了依赖注入,但是却使用了不同方法。
对于EJB 3.0,基于标准的方案、注解的广泛使用、与应用服务器的高度整合都使得EJB 3.0拥有更好的厂商无关性、更高的开发效率。依赖注入和集中的xml配置文件协调一致的使用,使开发者能够构建更有弹性的应用,并且可以同时与几个应用服务提供者一起协作。
1.5.3 使用EJB 3.0简化EJB开发
如果用过以前的规范开发EJB,就会发现开发一个类似于HelloWorldEJB这样简单的EJB有多困难。至少需要两个接口、一个Bean类和一个部署描述符。大多数开发人员都在想:我要这些干什么?在Oracle Jdeveloper, Eclipse和XDoclet等IDE中,开发人员可以轻松地完成这些琐事,但是,在将EJB部署到所选容器之前,开发人员仍需负责编译这些类并包装部署描述符。
EJB 3.0使用以下方法来克服这种复杂性:
无需使用接口和部署描述符,而是由容器使用元数据标注生成。
将普通Java类用做EJB,将普通业务接口用于EJB。
简化容器管理的持久性,EJB 3.0对CMP实体Bean进行了全面的改革,以吸引开发人员的注意力。持久性框架(如OracleAS TopLink)、开放源码的Hibernate已成为开发J2EE应用程序持久性框架的宠儿,而实体Bean由于既复杂又沉重,已不再受欢迎。EJB 3.0采用了一个类似TopLink和Hibernate的轻量级持久性模型,以简化容器管理的持久性,而这对开发人员而言无疑很有诱惑力。我们来简单了解一下该实体Bean计划关于持久性改进方面的详细内容,实体Bean正在作为POJO而重获新生,也将不再需要组件接口。现在实体Bean将被视为纯粹的对象,因为它也将支持继承性和多态性。
简化EJB的客户端视图。
使用EJB(即查找和调用)非常复杂,即使在应用程序中已经对其进行了配置,J2EE 1.4和EJB 3.0规范正是要简化EJB的客户端视图。若现在就想使用EJB,则必须在部署描述符中定义ejb-ref或ejb-local-ref,查找EJB然后再调用。EJB 3.0建议使用另一种方法,即使用setter注入来查找和调用EJB。
1.6 EJB 3.0应用前景
EJB经过了长达8年的卧薪尝胆,被Sun称为最简单的EJB 3.0框架终于在今天正式推出了。也许是Sun意识到了自己的失误,在制定EJB规范时将以前烦琐的部分基本都已经去掉了。EJB 3.0看起来就好像新的框架一样,这一点从它的规范就可以看出,EJB 3.0的规范文件比EJB 2.0规范文件的尺寸小得多。为了使EJB的开发更加容易,EJB规范组织作出的努力是有目共睹的。就像他们说的那样,一切都会变得简单,但做到这一点并不容易。目前已经定义了50个注释标记(还有几个将在下一个草案中发布),每一个都有自己的默认规则和其他操作。
EJB 3.0和Java EE 5几乎是同时发布的,因此,EJB 3.0中使用了很多Java EE 5的新特性。如EJB 3.0在定义Bean时(包括会话Bean和实体Bean),不再使用各种各样的接口,而是使用Java EE 5提供的注释(annotations)进行定义,无论什么样的企业级Bean只是一个加了相应注释的简单的Java对象(POJO)。不仅如此,EJB 3.0已经全面使用注释取代了接口,如定义Bean的业务接口、O/R映射信息、资源引用信息等都使用注释进行描述。由于Hibernate的创始人Gavin King加入了EJB小组,负责制定EJB的O/R映射规范,因此,EJB 3.0的O/R映射十分类似Hibernate,这使得熟悉Hibernate的开发人员学习EJB 3.0非常容易,这也说明EJB 3.0正在和Hibernate走向溶合。同时,Hibernate也提供了两套API,一套是Hibernate本身的API,另外一套是和EJB 3.0兼容的API。也就是说,只要使用Hibernate第二套API,就可以很容易地将其使用Hibernate的程序移植到EJB 3.0上。虽然EJB 3.0刚刚发布,但已经有很多EJB服务器支持它了,其中跟得最紧的是JBoss,之后WebLogic, WebSphere等也随之跟进。因此,各大厂商对EJB 3.0还是非常看好的。
自从那些如Struts, Hibernate, Spring等轻量级的框架开始在市面上出现并流行后,很多开发人员开始跟随着这些开源大师的指挥棒方向前进,EJB开始逐渐从人们的视线中淡出。但EJB 3.0的问世又将人们的视线拉了回来。毕竟,EJB出自Sun公司,如果它也能像Hibernate,JDO一样容易使用,那它是非常有前途的(至少我是这么认为的)。现在EJB 3.0已经和Hibernate在O/R映射上非常相似了,在未来,EJB 3.0也许将成为轻量级框架的一员,让我们拭目以待吧!
1.7 小结
本章首先介绍了EJB的基本概念、基础结构和体系结构,使读者对EJB有了基本的了解,讨论了EJB和JavaBean的区别,以及EJB的优缺点。然后介绍了EJB 3.0的新规范,比较了EJB 3.0和以前版本的重要改进,比较了EJB 3.0和Spring的优缺点,及EJB 3.0如何简化EJB的开发过程。最后展望了EJB 3.0的应用前景,使读者对EJB有了更深刻的认识。