430 likes | 625 Views
声明. 本课件仅用于北京航空航天大学计算机学院的教学; 本课件修改采用了一些网络资源(论文、研究报告、技术报告等),在采用的时候并没有准确标注引用信息。. 有关 J2EE 的流言. 1. 关系 - 对象映射 2.EJB 的简化 3. 轻量级框架和容器. 1. 对象 - 关系映射. 对象 - 关系映射 ( Object/Relation Mapping ,简称 ORM ) 面向对象的开发方法是当今企业级应用开发环境中的主流开发方法 关系数据库是 企业级应用环境中永久存放数据的主流数据存储系统 对象和关系数据是业务实体的两种表现形式 业务实体在内存中表现为对象
E N D
声明 • 本课件仅用于北京航空航天大学计算机学院的教学; • 本课件修改采用了一些网络资源(论文、研究报告、技术报告等),在采用的时候并没有准确标注引用信息。
1.关系-对象映射 • 2.EJB的简化 • 3.轻量级框架和容器
1. 对象-关系映射 • 对象-关系映射 (Object/Relation Mapping,简称ORM) • 面向对象的开发方法是当今企业级应用开发环境中的主流开发方法 • 关系数据库是 企业级应用环境中永久存放数据的主流数据存储系统 • 对象和关系数据是业务实体的两种表现形式 • 业务实体在内存中表现为对象 • 在数据库中表现为关系数据
数据持久化(对象-关系映射)方法: • Entity Bean • 会话 bean 和 JDBC • JDO(Java Data Object) • Hibernate • iBatis , TopLink, ……
Entity Bean • 实体Bean作为企业数据持久性机制具有如下优点: • 标准化 • 透明的持久性 • 事务支持 • 基于组件的设计 • 容器管理的服务。EJB 容器使开发人员不必管理常见的企业功能,如安全性、事务处理、连接合用和外部资源管理。
实体 bean 的主要缺点: • 设计复杂性。容器管理的服务和自动且透明的持久性为整个应用程序设计引入了复杂性。实体 bean 通常也可以通过会话 bean 来访问,这意味着每个事务至少包含两个企业 bean,而通常会更多。 • 构建周期很长。一个 EJB 周期(设计/构建/测试/集成/测试/部署)所花的时间比可比的 Java 持久性解决方案多两到三倍。 • 响应时间。实体 bean 与 bean 实例的粒度相关。因此,开发人员始终面临选择:要么将 bean 按原样装入,而在别的方面解决响应时间长的问题;要么将数据分解成较小的实体,从而创建更复杂的系统体系结构。 • 资源使用情况。实体 bean 消耗了大量系统资源。
会话 bean 和 JDBC • 会话 bean 和 JDBC 组合最受认可的优点如下: • 设计简单。从体系结构设计观点来看,通过会话 bean 来直接处理数据管理比使用实体 bean 简单得多。 • 细粒度的控制。因为会话 bean 是通用的工作程序组件,所以它们允许开发人员完全控制整个持久性过程,包括高速缓存、持久性、并发性、同步及其它。相反,CMP 实体 bean 不允许开发人员控制持久性机制,而 BMP 实体 bean 仅使开发人员能够定义应发生什么,而不能定义应在何时或什么样的情况下发生。 • 成熟。JDBC 存在了七年左右!而实体 bean 出现至今只有三年多的时间。JDBC 的可靠性和最佳实践对于 J2EE 持久性机制的开发来说是一笔非常宝贵的资产。 • 速度。因为开发人员完全控制在会话 bean 中使用的数据访问机制,所以可以针对某些任务优化数据访问和持久性逻辑。由于直接和有目的的操作,这可以产生极快的响应时间。
缺点如下: • 实现复杂。虽然这种系统的体系结构设计相当简单,但实际的会话 bean 实现常常十分复杂。如果应用程序的数据需求相当复杂,那么可能就无法实现管理数据库连接、确保数据完整性以及正确处理事务语义这些关键任务。开发人员常常需要实现某种类型的高速缓存同时确保最优性能。构造这种高速缓存机制使该系统的开发和维护进一步复杂化。 • 非固有的事务性。实体 bean 本质上是事务性组件,这些组件具有可配置的事务语义;而会话 bean 没有。将事务语义直接编码到应用程序代码中时,开发人员必须处处小心以确保每个功能的业务规则、流控制和事务完整性都得以保护并且可以容错。在实体 bean 开发中,这些细节都由容器处理。 • 持久性不是自动的或者得不到保证。在实体 bean 操作中,容器处理 bean 状态的持久性,并确保这种数据得到保护,供以后使用。对于会话 bean,将数据保持在安全、长期的数据存储中是开发人员的责任。
JDO(Java Data Object) • JDO 提供了面向对象的持久数据存储。开发人员使用 POJO(无格式普通 Java 对象,plain ordinary Java object)来装入和存储持久数据。 • 会话 bean 与 JDO 结合类似于将它们与 JDBC 结合,但 JDO 是以更面向对象且更以 Java 为中心的观点处理该问题的。
会话 bean 和 JDO 提供了许多优点,其中有些来自会话 bean,而其它的来自 JDO,使用会话 bean 而不使用实体 bean 进行交付的主要优点有两个: • 设计简单。从体系结构设计的观点来看,直接通过会话 bean 来处理数据管理比使用实体 bean 简单得多。 • 细粒度控制。因为会话 bean 是通用的工作程序组件,所以它们允许开发人员对整个持久性进程进行完全控制,包括高速缓存、持久性、并发性和同步等。
这两个优点并不是会话 bean/JDO 组合所特有的:会话 bean 与 JDBC 的结对也存在这两个优点。但是,JDO 确实提供了一些独特的优点: • 编码简单。JDO 体系结构向开发人员隐藏了低级别的持久性细节,从而使他们专注于从业务过程的角度管理对象,不至于陷入数据持久性逻辑的琐碎细节中。 • 提高的生产力。JDO 程序员能完全在面向对象的范例内操作。这通常会使开发更简洁、更平滑且更不易出错,因为程序员不用在关系的思想体系和面向对象的思想体系之间频繁地转换。 • 面向对象的持久性。JDO 的面向对象本质不仅提高了开发人员生产力,而且它还考虑到比关系持久性所提供的还要丰富的持久性机制。JDO 并不仅仅使 Java 对象持久;它还透明地处理整个相关对象图的持久性。因此,当实例被持久存储时,它所维护的对其它对象实例的任何内部引用也都被持久存储(除非它们已被声明为瞬态)。JDO 还存储类型层次结构的完整信息,并能根据类型(父类和接口)实现请求,而不是只了解持久实例的特定局部类型。
缺点: • JDO 不成熟。JDO 还处于初期。到编写本文时,JDO 1.0 规范的发布还不到一年。其结果是,JDO 社区还非常小,最大且最具威望的 JDO 门户网站可以炫耀的也只是其会员有五千多一点。尽管这些数据并不表示 JDO 是一种差劲的技术,但它们确实表明它还处于前沿。几乎没有几家公司愿意尝试在业务级实现中使用 JDO。 • 会话 bean 不是事务性的。J2EE 客户机不能直接访问 JDO 对象。必须由 servlet 或会话 bean 处理进入请求。因此,尽管很容易将 JDO 对象声明为事务性的,但仍必须使用非事务性组件来访问它们。在将事务语义直接编码到会话 bean 的应用程序代码中时,开发人员必须尽一切可能确保每个功能的业务规则、流程控制和事务完整性都得以保留并是容错的。尽管使用容器管理的事务可以极大地缓解这一问题,但是这样做限制了开发人员对持久性进程的控制,并除去了许多控制事务粒度所产生的体系结构上的灵活性。
Hibernate • Persistent Object是简单的业务实体对象(要被持久化的对象)。通过hibernate被透明的持久化到数据库中。
表person • 开发一个Person类:它和普通的类没有什么不同,但它符合bean的规范,即为每个属性提供get,set方法 • 用xml映射文件描述其映射数据库的方式 • 编写Person.hbm.xml( 建议命名为:“类名”+“hbm.xml”),并且放置在Person类相同包目录下 • 通过hibernate api对其持久化
为了运行要配置数据库:以mysql数据库为例子:(只用劳动1次即可)为了运行要配置数据库:以mysql数据库为例子:(只用劳动1次即可) • hibernate.properties 在hibernate源程序的根目录可以找到此文件模板,copy到我们的类的根目录。即:“..\h” • 该文件前两项都填好了,只用填数据库连接和username,password hibernate.dialect net.sf.hibernate.dialect.MySQLDialect hibernate.connection.driver_class org.gjt.mm.mysql.Driver hibernate.connection.url jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=GBK hibernate.connection.username root hibernate.connection.password
2. EJB 3.0 • EJB1.0规范是在1997年提出,当时是为了解决CORBA的复杂性而提出的。EJB1.0规范中曾经承诺: • EJB将简化开发人员的开发,开发人员不必理会系统底层的事物和状态管理细节,以及多线程,资源池和其他的一些复杂的APIs。 • EJB将遵循java的一次编写,处处运行的原则,EJB只需要编写一次,就能够不加修改的和不需要重新编译的部署到其他任意平台。
然而在实际应用中,EJB并没有兑现以上承诺。EJB从1.0规范到2.0规范变得越来越复杂,开发一个EJB必须实现一些与业务层无关的接口,还要维护冗长的XML配置文件。同时,各个厂商还加入了一些自己特定的特性,当开发系统时使用了这些特性后,将无法实现各个服务器之间的平滑移植。然而在实际应用中,EJB并没有兑现以上承诺。EJB从1.0规范到2.0规范变得越来越复杂,开发一个EJB必须实现一些与业务层无关的接口,还要维护冗长的XML配置文件。同时,各个厂商还加入了一些自己特定的特性,当开发系统时使用了这些特性后,将无法实现各个服务器之间的平滑移植。 • 为此,专家组对原有的EJB规范进行了大刀阔斧的改进,提出了新的EJB3.0规范。
EJB 2.x的复杂性 • 冗长的XML配置文件 • EJB的所有配置信息都在XML文件中,如一个EJB的local接口,remote接口和Bean实现类的指定,都是通过ejb-jar.xml中配置来完成的。由于XML文件是基于文本的,所以开发者在编写XML文件的时候往往会犯一些小错误的,例如:大小写,多写,漏写字母等。但是这种简单的错误在编译的时候是不会被发现的,只要在部署的时候才能发现。这样使得调试EJB变得非常的繁琐,降低了开发效率。 • 开发EJB不但要编写ejb=jar.xml文件,还需要一个应用服务器特有的部署描述文件。然而,每个厂商的应用服务部署服务器的配置文件都是不同的,这样就带来一个问题,当把一个EJB应用从一个服务器移植到另一个服务器时,就必须修改相应的配置文件。
EJB的实现类不是POJO( Plain Ordinary Java Objects ) • 在EJB2.x规范下开发EJB,即便是最简单的helloworld程序,我们都至少编写3个类文件,分别是Bean类,Home接口和Remote接口。这些类和接口必须继承或实现规范中所定义的接口,并且要实现一些在业务逻辑中不会使用的回调函数,如ejbCreate(),ejbPassivate()等等。
EJB的复杂调用过程 • 在EJB2.x规范下,标准的EJB调用过程可以描述为:调用者通过JNDI查找想要调用的EJB的home接口,使用者通过返回的home接口创建相应的remote接口,通过返回的remote接口才能调用实现业务逻辑的方法,这样的调用过程不仅在远程调用时使用,即使在同一容器的jsp中,甚至在一个ejb调用另外一个ejb时,都要进行同样的调用过程。
实体Bean的局限性 • 在EJB2.x规范中,实体bean分为两种:CMP和BMP。BMP由于需要用户去维护持久化工作和维护EJB状态,开发者需要做很多繁琐工作。与传统的JDBC方式相比,并没有带来太多的便利,同时还要实现不必要的容器组件接口。
EJB3.0的简洁性 • EJB3.0规范中主要涉及两个方面的改变: • 一套以注释为基础的EJB编程模型,再加上EJB2.1中定义的通过布署描述符合几个接口定义的应用程序行为。 • 新的实体Bean持久化模型,EJBQL也有许多重要的改变。
EJB注释: • EJB规范组织一个重要的目标是减轻原始代码的数量,并且他们为此给出了一个相当简洁的办法。 • 在EJB3.0里,任何类型的企业级Bean只是一个加了适当注释的简单Java对象(POJO)。 • 注释可以用于定义bean的业务接口、O/R映射信息、资源引用信息,效果与在EJB2.1中定义部署描述符和接口是一样的。 • 在EJB3.0中部署描述符不再是必须的了,home接口也没有了,也不必实现业务接口(容器可以为你完成这些事情)。 • 比如, • 可以使用@Stateless注释标记类把Java类声明为一个无状态会话bean。 • 对于有状态会话bean来说,@Remove注释可以用来标记一个特定的方法,通过这个注释来说明在调用这个方法之后bean的实例将被清除掉。 • 为了减少描述组件的说明信息,规范组织还采纳了由异常进行配置(configuration-by-exception)的手段,意思是你可以为所有的注释提供一个明确的缺省值,这样多数常规信息就可以据此推断得出。
在EJB3.0规范中,写一个无状态会话bean只需要一个简单的Java文件并在类层加上@Stateless注释就可以了。在EJB3.0规范中,写一个无状态会话bean只需要一个简单的Java文件并在类层加上@Stateless注释就可以了。 • 这个bean可以扩展javax.ejb.SessionBean接口,但这些不是必须的。 • 一个无状态会话bean不再需要home接口,没有哪类EJB再需要它了。 • Bean类可以实现业务接口也可以不实现它。 • 如果没有实现任何业务接口,业务接口会由任意public的方法产生。 • 如果只有几个业务方法会被暴露在业务接口中,这些方法可以使用@BusinessMethod注释。 • 缺省情况下所有产生的接口都是local(本地)接口,你也可以使用@Remote注释来声明这个接口为remote(远程)接口。
import javax.ejb.*; /*** A stateless session bean requesting that a remote * business interface be generated for it.*/@Stateless@Remotepublic class HelloWorldBean { public String sayHello() { return "Hello World"; }}
3. 轻量级框架和容器 • 自从EJB规范成型以来,很多事情已经变了样 • EJB规范中的一些部分已经过时。 • EJB和RMI之间那种传统的紧密关系也开始显得有些不合时宜,这一方面是因为web services的迅速发展,另一方面是因为人们发现:很多时候EJB只需要本地接口。 • EJB最善于实现业务对象分布的体系结构,然而这种体系结构究竟有多大程度的普遍性,如今看来是相当值得怀疑的。 expert one-on-one J2EETM Development without 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环境下实现。
只有在提供远程调用(remoting)这件事上,EJB是标准J2EE架构中唯一的实现方式。不过,正如我们将要看到的,只有在RMI/IIOP远程调用的领域里,EJB才算得上一种出色的实现技术只有在提供远程调用(remoting)这件事上,EJB是标准J2EE架构中唯一的实现方式。不过,正如我们将要看到的,只有在RMI/IIOP远程调用的领域里,EJB才算得上一种出色的实现技术 • 对于那些真正需要对象分布的应用,EJB仍然是上佳之选——尤其是当它们完全用Java实现、或者用IIOP作为通信协议时。不过,这种应用比人们通常想象的要罕见得多。 • EJB不是J2EE的全部。即便使用没有EJB的J2EE,我们也无须重新发明轮子——我们不必重新实现J2EE已经提供的服务,只是改变使用它们的方式而已。
轻量级框架 • Spring • Pico • HiveMind
More Application Layering Combinations • Presentation/Business/Persistence • Struts+Spring+Hibernate • Struts + Spring + EJB • JavaServer Faces + Spring + iBATIS • Spring + Spring + JDO • Flex + Spring + Hibernate • Struts + Spring + JDBC • You decide…
EJB (<=2.x) in the Service Layer • Sun’s traditional solution to middle tier business logic • Specification that did not always work as projected in real applications. • EJBs are less portable than POJO based architectures. • Inconsistencies by vendors make EJBs break the “write once, run anywhere” rule. • Fosters over-engineering in most cases • Entity Beans – very limiting compared to alternatives such as Hibernate. • Performance with POJOs are much faster then EJBs. • EJBs run in a heavy container • Your code becomes coupled to EJB API. • We need to redefine what J2EE means…
Spring - IOC • IOC,即控制反转模式(也称作依赖性介入),这是Spring的核心,也是精髓。 • 其基本概念是: • 不创建对象,但是描述创建它们的方式。 • 在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。 • 容器 (在 Spring 框架中是 IOC 容器) 负责将这些联系在一起。
传统的程序开发,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。 • IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。 • 这一点是通过DI(Dependency Injection,依赖注入)来实现的。
在典型的 IOC 场景中,容器创建了所有对象,并设置必要的属性将它们连接在一起,决定什么时间调用方法。 • 下表列出了 IOC 的一个实现模式。 Spring 框架的 IOC 容器采用类型 2 和类型3 实现。
Middle Tier: Setter Injection public class ServiceImpl implements Service { private int timeout; private AccountDao accountDao; public void setTimeout(int timeout) { this.timeout = timeout; } public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } // Business methods from Service … <bean id="service" class="com.mycompany.service.ServiceImpl"> <property name="timeout"><value>30</value></property> <property name="accountDao"><ref local="accountDao"/></property> </bean>
Middle Tier: Constructor Injection public class ServiceImpl implements Service { private int timeout; private AccountDao accountDao; public ServiceImpl (int timeout, AccountDao accountDao) { this.timeout = timeout; this.accountDao = accountDao; } // Business methods from Service <bean id="service" class="com.mycompany.service.ServiceImpl"> <constructor-arg><value>30</value></constructor-arg> <constructor-arg><ref local="accountDao"/></constructor-arg> </bean>
Spring - AOP • 面向方面的编程,即 AOP,是一种编程技术,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。AOP 的核心构造是方面,它将那些影响多个类的行为封装到可重用的模块中。