使用JSF, Spring, Hibernate构建一个实际的web应用
概要:
使用JSF, Spring, Hibernate构建一个实际的web应用并不是毫无意义的事情。本文向你展示了如何整合JSF, Spring和Hibernate,以及使用这些技术构建实际web应用的最佳实践和设计方针。
作者: Derek Yang Shen
翻译: rongsantang
JavaServerFaces (JSF)技术是面向J2EE应用的新的UI(user interface)框架。它天生地非常地适用于基于MVC架构的应用程序。无数的文章已经介绍过JSF,然而大多数都是很理论地介绍,并没有涉及到实际 企业应用开发遇到挑战。很多的问题并没有解决,比如:JSF如何融于总的MVC体系结构中?JSF如何同其他的Java框架整合?业务逻辑是否应该存在于 JSF的backing beans中?你在JSF中如何处理安全问题?最重要的是你如何使用JSF构建一个实际的web应用?
本文将涉及所 有的这些问题。它将向你展示如何整合JSF同其他的Java框架(也就是Spring和Hibernate)。本文展示了如何创建一个在线的产品目录系统 ---JCatalog web应用程序。通过JCatalog这个例子,这篇文章覆盖了Web应用程序设计的每一个阶段,包括业务需求收集,分析,技术选择,总体架构和实现级别 的设计。这篇文章讨论了JCatalog中使用的技术的优缺点,展示了应用程序关键方面的设计方法。
这篇文章是面向Java架构师和基于J2EE的web应用程序开发者。它并不会介绍JSF, Spring和Hibernate,如果你不是很熟悉,请查看资源一栏。
一,示例程序的功能需求
本文的示例程序JCatalog是一个实际的Web应用程序,对于我们讨论Web应用程序的体系结构来说,它是足够现实的。我们以介绍JCatalog的需求开始,在贯穿本文的技术抉择和体系结构设计中,我们会经常返回查阅本节的内容。
设 计一个Web应用的第一步就是收集系统的功能需求。示例程序是一个典型的e-business应用系统。用户可以浏览一个产品目录,查看产品的详细信息; 管理员则可以管理产品目录。作为扩展,存货管理和订单处理可以被加入应用使应用成为一个成熟的e-business系统。
Use cases(用例)
用例分析是用来了解示例程序的功能需求。图一就是该应用的用例图。
一个用例图确定了在系统中的参与者以及它们能够执行的操作,七个用例必须在示例程序中实现。User可以浏览产品目录,查看产品的详细信息。如果一个User登陆了系统,他就变成了一个Administrator,就可以创建新的产品,编辑已存在的产品,删除老的产品。
Business rules(业务规则)
JCatalog必须符合以下的业务规则:
l 每一个产品都有一个唯一的产品ID;
l 每一个产品至少属于一个类别;
l 产品ID一旦创建就不能改变;
Assumptions(假定)
对于系统的设计和实现我们做以下的假定:
l 英语是默认的语言;不需要国际化支持;
l 在目录中不会有超过500个商品;
l 目录的更新不频繁;
Page flow(页面转移)
图二显示了JCatalog的页面以及页面间的跳转。
应用程序有两组页面:public Internet和administration intranet。Intranet仅仅对成功登录系统的users是可见的。ProductSummary不会作为一个单独的页面呈现给users,它 是作为Catalog页面中的一个HTML frame呈现的。ProductList是一个只能被administrator可见的特殊目录,它包含了创建,编辑,删除商品的连接。
下图是一个Catalog页面的mock-up。理想的情况下,对于每一个页面,在需求文档中都要有一个包含页面需要的所有的控制信息和内容的mock-up页面。
Put JSF to work 2
二,High-level architecture design(总体架构设计)
设计Web应用系统的下一步是总体的架构设计。它包括将应用程序细分为功能组件,将这些组件划分成若干层。总体架构设计对于具体技术使用是中立的。
Multitiered architecture(多层架构)
多层架构把整个系统划分成明显的功能单元:客户端,表示层,业务逻辑,综合(Integration),EIS。这种架构确保了责任的明确划分,使系统更加易于维护和扩展。三层或多层系统被证明比没有业务逻辑层的c/s系统更加灵活和可扩展。
客户层是数据模型被消费和表示的地方。对于一个Web应用来说,客户层通常是Web浏览器。基于浏览器的瘦客户端没有包含表示逻辑,它要依靠于表示层。
表示层将业务逻辑层的服务暴露给用户。它知道如何处理一个客户端的请求,如何同业务逻辑层交互,如何选择下一个view去显示。
业务逻辑层包含了一个应用程序的业务对象和业务服务。它从表示层收到请求,根据请求处理响应的业务逻辑。业务逻辑层组件大大受益于系统级服务(比如安全管理,事务管理,资源管理)。
集成层是业务逻辑层和EIS(Enterprise Information System)层之间的一座桥梁。它把同EIS层交互的逻辑封装起来。有时候把集成层和业务逻辑层合起来称作中间层。
应用程序数据在EIS层持久化。它包括关系数据库,对象数据库和遗留系统。
JCatalog的架构设计
下图显示了JCatalog的总体架构设计以及它如何实现多层体系结构。
应 用程序使用了一个多层非分布式的框架,上图向我们显示了应用层次是如何划分的,每一层使用的具体技术。这张图同时作为示例应用程序的部署图。对于一个配置 的架构,表示层,业务逻辑层,集成层都位于同一个Web容器中。定义良好的接口隔离每一层的职责。配置的架构让应用简单,可扩展。
对于表示层,经验告诉我们最佳的方法是选择一个已经存在的,经过考验的Web应用框架,而不是自己设计和构建一个框架。我们有一些Web应用框架可供选择,比如Struts, WebWork, JSF。我们为JCatalog选择JSF作为表示层框架。
无论EJB还是POJO都可以用来构建业务逻辑层。如果应用程序是分布式的,则拥有远程接口的EJB是一个很好的选择。而我们的JCatalog是一个典型的没有远程访问需求的Web应用,所以在Spring框架下的POJO被我们用来实现业务逻辑层。
集成层在关系数据库上处理数据的持久化工作。有不同的方案可以用来实现集成层:
lPure JDBC:这是最灵活的方案;然而底层的JDBC用起来十分笨重,而且劣质的JDBC代码性能也不好。
lEntity beans:对于隔离数据访问代码和处理O/R映射数据持久化,CMP是一个很昂贵的方案。它是一个以app server为中心的方案。一个entity bean不会使应用依赖于某个数据库,却会让应用依赖于某个EJB容器。
lO/R mapping框架:O/R mapping框架是一个以对象为中心的实现数据持久化的方案。以对象为中心的应用很容易开发而且非常的轻便。在这个领域有不少的现成框架:JDO, Hibernate, TopLink, CocoBase等等。我们在本应用中使用Hibernate。
现在我们结合每一层来讨论一下具体的设计问题。因为JSF是相对新的技术,我们会重点讨论它的。
表示层和JSF
表示层收集用户的输入,表示数据,控制页面导航,将用户输入委托给业务逻辑层。表示层也能够验证用户输入和维护应用会话状态。在下面我们会讨论表示层的设计考虑事项和模式,以及为什么我们选择JSF来实现JCatalog的表示层。
Model-View-Controller
MVC是Java蓝皮书强烈建议的交互型应用程序使用的结构设计模式。MVC分割设计关注,从而能够减少代码的重叠,集中控制,使应用更加可扩展。MVC同时帮助不同技能的开发者集中于他们擅长的技能方面,通过清晰定义的接口合作在一起。MVC是表示层的结构设计模式。
JavaServer Faces
JSF 是面向基于Java的Web应用而开发的server-side的UI组件框架。JSF包括了一组API,这些API用来表现UI组件以及保持它们状态; 处理事件,服务器端的验证,以及数据转换;定义页面导航;支持国际化和accessibility;以及对这些功能提供可扩展性。同时它还包括两个JSP custom tag libraryies,用来在JSP页面中表示UI组和关联组件与服务器端对象。(实际上JSF现在是一个规范和一组接口以及他提供的参考实现,你也可以 自己做你自己的JSF实现,当然难度比较大,如果后面没特指的话“实现”指的就是自带的参考实现)
JSF和MVC
JSF非常适用于基于MVC的表示层框架。它对行为和表示有着清晰的划分。它支持我们熟悉的UI组件和Web层的概念,却不会把你限制在某些脚本技术或标记语言上。
JSF 的backing beans是model层(更多关于backing beans在后面的章节)。它们也可以包含动作,这些动作是作为控制器层的一个扩展以及把用户的请求代理给业务逻辑层。请注意,从整个应用程序的框架来 看,业务逻辑层也常常被称为model层。(注意和这里的model层区别开)包含JSF标签的JSP页面是作为View层。而Faces Servlet则提供controller的功能。
为什么使用JSF?
JSF不仅仅是另一个Web框架,下面是它与一般的Web框架的不同:
l象Swing一样的面向对象的Web应用开发:服务器端声明的,有event listeners和handlers的UI组件模型(就像Swing的组件),促使能够面向对象的Web应用开发。
lBacking-bean management:Backing bean是在页面中与UI组件关联对应的JavaBeans。Backing bean management将UI组件对象的定义,与保持数据执行应用相关处理的对象区分开来。JSF的具体实现在恰当的范围内储存和管理这些backing- bean的实例。
l可扩展的UI组件模型:组成JSF应用的JSF UI组件是可配置,可重用的元素。你可以继承这些标准的UI组件来开发更为复杂的组件,比如menu bar,tree组件等等。
l 灵活的表现模型:Renderer把UI组件的功能和它的view分开。不同的Renderer可以被创造出来,用来定义同一种客户端或不同客户端的同一 个组件的不同的外观。(简单介绍一下,也就是说你可以定义HTMLRenderer, WMLRenderer来对同一组件生成HTML和WML格式的外观。)
l可扩展的转换和验证模型:你可以在标准的converter和validator的基础上开发你的converter和validator提供更强大的功能。
尽管JSF很强大,但它现在还不成熟。JSF自带的component, converter, validator是很基本简单的。而且每一个组件一个的validation model还不能处理组件和validator之间多对多的validation。JSF标签同JSTL还不能无缝连接。
在下面的章节中,我们将讨论用JSF实现JCatalog的关键部分和设计决定。首先我们讨论一下JSF中managed bean和backing bean的定义和使用。然后再介绍在JSF中如何处理安全,分页,caching,文件上传,验证以及定制的错误信息。
Managed bean, backing bean, view object, and domain object model
JSF 引入了两个新概念:managed bean和backing bean。JSF提供了强大的管理bean的机制。一个被JSF管理的JavaBean对象叫做managed bean。一个managed bean描述了一个bean如何创建和管理的,这些和bean的功能无关。
Backing bean定义了页面上的UI组件的属性和处理逻辑。每一个backing bean的属性对应一个组件或者组件的值。Backing bean同时定义了一组执行组件功能的方法,比如验证组件的数据,处理组件触发的事件,当组件activate时处理与导航相关的操作。
一个典型的JSF应用中的每一页面都有一个backing bean。然而,实际中强制页面和backing bean的一对一关系不是一个好的做法。它会导致类似代码重复的问题。实际情况中,一些页面也许会共享同一个backing bean。例如在JCatalog中,CreateProduct和EditProduct页面共享同一个ProductBean定义。
一个View对象是一个只在表示层使用的model对象。它包含着必须在View层显示的数据,包含着验证用户输入,处理事件,同业务逻辑层交互的 逻辑。在基于JSF的应用中,backing bean就是view对象。在本文中,backing bean和view对象是可互换的概念。
与Struts 中的ActionForm和Action概念相比,使用JSF中的backing bean开发更加符合OO设计习惯。一个backing bean不仅仅包含显示数据,还包括与这些数据相关的行为。而在Struts中,ActionForm和Action分别包含数据和逻辑。
我们大家都听说过domain object model(域对象模型)。那么domain object model和view object有什么不同呢?在一个简单的Web应用中,一个域对象模型经常穿越所有的层使用。然而在稍复杂的Web应用中,一个独立的view object是很需要的。Domain object model是关于业务对象(BO)的,应该属于业务逻辑层。它包含业务数据和与特定业务对象关联的业务逻辑。一个view object包含着显示相关的数据和行为。JCatalog的ProductListBean就是view object的一个好例子。它包含着表示层的数据和逻辑,比如分页相关的数据和逻辑。将view object和domain object model分开的一个缺点就是必须在两个对象模型之间进行data mapping。在JCatalog中,ProductBeanBuidler和UserBeanBuilder使用了基于反射的Commons- BeanUtils包来实现data mapping。
安全
目前,JSF并没有内建的安全特性。示例应用的安全需求是很简单的:仅当用户要登录到administration intranet时需要基于用户名密码的认证,而且不需要授权。
对于在JSF中的用户认证,有以下方案:
l使用一个backing bean基类:这各方案很简单,但是会让backing beans依赖于这个继承结构。(也就是backing bean都继承这个基类)
l使用一个JSF ViewHandler包装类:这个方案会把安全逻辑紧紧地限制在JSF这个特殊的Web层技术上。
l使用servlet filter:一个JSF应用和其他的基于Java的Web应用没什么区别,因此一个filter就是处理认证检查的最好地方。这种方案安全逻辑不会绑定到特定Web应用上。
在示例应用中,SecurityFilter类处理用户的认证。目前,受保护的资源只包括三个页面,所以为了简单起见,把它们的位置硬编码到Filter类里面了,作为改进你可以把具体的安全规则和受保护的资源写入配置文件中。
分页
应用中,Catalog页面需要分页。表示层可以处理分页,同时意味着所有的数据必须在表示层取得和存储。分页也可以在业务逻辑层, 集成层甚至EIS层处理。JCatalog的一个假定就是只有不超过500个产品在目录中。所有的产品信息都可以放入用户的session中。分页逻辑做 在ProductListBean里面。与分页相关的参数product per page通过JSF managed-bean机制可以配置。
Caching
Caching是在Web应用中提高性能的最重要的技术之一。在应用框架中,Caching可以在许多层中实现。若结构中 一层能够避免对在它之下的层的调用就是最理想的状况(??)。JSF managed-bean机制使得在表示层Caching非常的容易。通过改变一个managed bean的scope,managed bean中的数据就能在不同的scope中被缓存。
示例应用使用了两级的Caching。第一次层的Caching在业务逻辑层里面。CachedCatalogServiceImpl类维护所有产 品和目录的读写cache。Spring把这个类作为一个Singleton的service bean管理。所以,第一层的cache是一个application-scopse的读写cache。
为了简化分页逻辑加快应用程序的速度,产品也被缓存在表示层中,作为范围是session scope。每一个用户在session中维护他自己的ProductListBean。这样做的代价是系统内存和陈旧的数据。在用户的session期 间,如果administrator更新目录,用户可能会看到陈旧的数据。然而,基于我们前面的假设,目录中不会超过500的商品,目录不会经常的更新, 所以我们可以忍受这些代价。
File upload
目前Sun的JSF参考实现并不支持文件上传。Struts有很好的文件上传功能,但是你要使用这个功能的话 Struts-Faces包就是必须的了。在JCatalog中,每一个产品都有相对应的一张照片。当用户创建了一个新的产品后,他必须上传与之对应的图 片。图片被存储在应用服务器的文件系统中,用productID作为图片名。
示例程序使用,Servlet,和Jakarta Commons的file-upload包来实现一个简单的文件上传功能。这个方案需要两个参数:产品图片的目录和图片上传的结果画面。它们可以在 ApplicationBean中进行配置。请查看FileUploadServlet类获得详细信息。
Validation
JSF自带的标准的validator是很基本的,不能满足很多实际中的需求。开发你自己的JSF validater是很简单的。作者在示例中用自定义标签开发了SelectedItemsRange validater。它用来验证UISelectMany组件选择的条目数:
id="selectedCategoryIds">
id="categories"/>
请在示例中获得更多信息。
Error-message customization
在JSF中,你可以为converter和validator创建resource boudles,定制错误信息。一个resource bundle在faces-config.xml中创建:
catalog.view.bundle.Messages
错误信息的key-value值加到Message.propertyes文件中:
#conversion error messages
javax.faces.component.UIInput.CONVERSION=Input data is not in the correct type.
#validation error messages
javax.faces.component.UIInput.REQUIRED=Required value is missing.
业务逻辑层和Spring框架
业务对象和业务服务位于业务逻辑层。一个业务对象不仅仅包含数据,同时也包含与这个对象相关的逻辑。在示例中,有三个业务对象:Product, Category, User。
业务服务与业务对象作用,并且提供高级别的业务逻辑。一个正式的业务接口应该被定义,它包含了能够被客户端直接调用的服务接口。在Spring框架 下的POJO实现了JCatalog的业务逻辑层。这里有两个业务services:CatalogService包含目录管理相关的业务逻辑; UserService包含用户管理逻辑。
Spring是基于反转控制(IoC)的概念上建立的。示例程序中使用到的Spring的特性有:
l使用application context管理bean:Spring能够有效地组织管理我们的中间层的对象,为我们处理plumbing。Spring能够消除Singleton模式的泛滥,促进良好的OO编程习惯,比如面向接口编程。
l 声明式的事务管理:Spring使用AOP(aspect-oriented programming)做到不使用EJB容器也能使用声明式事务管理。通过这种方法,事务管理能够施加于任何POJO上。Spring的事务管理并没有 依赖于JTA,它使用其他的事务策略工作。在示例中,我们将在Hibernate的事务中使用声明式事务管理。
l数据访问异常的继承体系:Spring提供了一个有意义的异常体系替代SQLException。要想使用Spring的异常体系,必须在Spring配置文件中定义Spring数据访问的exception translator:
"org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator">
在示例程序中,如果一个ID重复的产品被插入的时候,DataIntegrityViolationException就会被抛出。这个异 常会被捕捉并且作为DuplicateProductIdException再被抛出。这样,DuplicateProductIdException就 能与其他数据访问异常区别开来进行处理。
l整合Hibernate:Spring并强制我们使用它强大的JDBC抽象的特性。它能够很好地同 O/R maping框架结合,尤其是Hibernate。Spring提供了有效安全的Hibernate session操作,Spring在application contexts中处理Hibernate SessionFactory的配置以及处理JDBC data sources,Spring还使应用程序很容易测试。
集成层和Hibernate
Hibernate是一个开源的O/R mapping框架,O/R mapping可以减少JDBC API的使用。Hibernate对所有的主流SQL数据库管理系统提供了支持。Hibernatede查询语言HQL是对SQL的最小的OO扩展,它为 对象和关系世界提供了一个优雅的桥梁。Hibernate提供了实现数据取得和更新,事务管理,数据库连接池,编程式或声明式的查询,声明式的实体关系管 理等等的机能。
Hibernate比其他的O/R mapping框架有更少的侵略性。反射,运行时二进制代码生成被使用。
生成SQL发生在系统启动的时候。它允许我们可以按照Java的习惯开发持久对象(PO),包括使用关联,继承,多态,组合以及Java Collections框架。示例应用中的业务对象(BO)就是POJO,不需要实现任何Hibernate相关的接口。
Data Access Object(DAO)
JCatalog 中使用了DAO模式。这个模式抽象和封装了所有对数据源的访问。应用程序有两个DAO接口:CatalogDao和UserDao。它们的实现类 HibernateCatalogDaoImpl和HibernateUserDaoImpl包含Hibernate特定的管理和持久化数据的逻辑。
Put JSF to work 3
三,设计的实现
现在我们看看如何将所有的东西连接起来,实现JCatalog。你可以下载全部的源代码。
http://www.javaworld.com/javaworld/jw-07-2004/jsf/jw-0719-jsf.zip
数据库设计
我们为示例应用创建了一个名为Catalog的schema,它由4个表组成,如下图所示:
类设计
下图是JCatalog的class图
面 向接口的编程贯穿整个设计。在表示层,4个backing bean被使用:ProductBean, ProductListBean, UserBean,和MessageBean。业务逻辑层包含2个业务服务(CatalogService和UserService)和3个业务对象 (Product, Category, User)。集成层包含2个DAO接口以及它们的Hibernate实现。Spring的application context连接和管理位于业务逻辑层和集成层的大部分对象bean。ServiceLocator将JSF和业务逻辑层结合起来。
将所有东西连接起来
由于文章篇幅所限,我们只分析一个use case。CreateProduct这个use case将向我们展示如何连接所有东西,创建应用程序。在我们深入细节之前,我们使用一个sequence图来示范所有层的集成:
现在我们来走一遍所有层,讨论如何实现CreateProduct的细节。
表示层
表示层的实现包括创建JSP页面,定义页面导航,创建和配置backing bean,以及结合JSF和业务逻辑层。
lJSP页面:createProduct.jsp是创建新产品的页面。它包含UI组件以及将组件连接到ProductBean。ValidateItemsRange这个自定义标签验证用户选择的种类数目。最后对于每一个新产品应该至少一个种类被选择。
l页面导航(page navigateon):应用程序的导航定义在应用的配置文件faces-navigation.xml中。CreateProduct的导航规则为:
<navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>createProduct</from-outcome> <to-view-id>/createProduct.jsp</to-view-id> </navigation-case></navigation-rule><navigation-rule> <from-view-id>/createProduct.jsp</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/uploadImage.jsp</to-view-id> </navigation-case> <navigation-case> <from-outcome>retry</from-outcome> <to-view-id>/createProduct.jsp</to-view-id> </navigation-case> <navigation-case> <from-outcome>cancel</from-outcome> <to-view-id>/productList.jsp</to-view-id> </navigation-case></navigation-rule>
lBacking bean:ProductBean不仅仅包含对应于页面上UI组件的属性,还包括了三个action:createAction, editAction, 和deleteAction。这里是createAction的代码:
public String createAction() { try { Product product = ProductBeanBuilder.createProduct(this); //Save the product. this.serviceLocator.getCatalogService().saveProduct(product); //Store the current product id inside the session bean. //For the use of image uploader. FacesUtils.getSessionBean().setCurrentProductId(this.id); //Remove the productList inside the cache. this.logger.debug("remove ProductListBean from cache"); FacesUtils.resetManagedBean(BeanNames.PRODUCT_LIST_BEAN); } catch (DuplicateProductIdException de) { String msg = "Product id already exists"; this.logger.info(msg); FacesUtils.addErrorMessage(msg); return NavigationResults.RETRY; } catch (Exception e) { String msg = "Could not save product"; this.logger.error(msg, e); FacesUtils.addErrorMessage(msg + ": Internal Error"); return NavigationResults.FAILURE; } String msg = "Product with id of " + this.id + " was created successfully."; this.logger.debug(msg); FacesUtils.addInfoMessage(msg); return NavigationResults.SUCCESS;}
在action中,一个Product业务对象(BO)基于ProductBean的属性而创建。ServiceLocator查找CatalogService。最后createProduct请求被代理给业务逻辑层的CatalogService。
lManaged-bean声明:ProductBean必须在JSF配置文件faces-managed-bean.xml中配置
<managed-bean> <description> Backing bean that contains product information. </description> <managed-bean-name>productBean</managed-bean-name> <managed-bean-class>catalog.view.bean.ProductBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>id</property-name> <value>#{param.productId}</value> </managed-property> <managed-property> <property-name>serviceLocator</property-name> <value>#{serviceLocatorBean}</value> </managed-property> </managed-bean>
ProductBean别设置为request范围,这就意味着如果JSP页面中有引用到ProductBean,那么JSF就会为每一个 request创建一个新的ProductBean。被管理的属性ID就会用request的参数productId来组装。JSF会从request得 到parameter,然后把值设置给managed property。
l表示层和业务逻辑层之间的连接:ServiceLocator抽象了查找service的逻辑。在示例中,ServiceLocator定义成一 个接口。ServiceLocatorBean作为一个JSF managed bean实现了ServiceLocator接口,它从Spring的application context寻找service:
ServletContext context = FacesUtils.getServletContext();this.appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(context);this.catalogService = (CatalogService)this.lookupService(CATALOG_SERVICE_BEAN_NAME);this.userService = (UserService)this.lookupService(USER_SERVICE_BEAN_NAME);
ServiceLocator在BaseBean中作为一个属性而定义。继承自BaseBean的这些managed bean都可以通过它查找服务,并且,JSF的managed bean机制能够将ServiceLocator的具体实现连接给这些managed bean。
业务逻辑层
这一层的任务有:定义业务对象(BO),创建service接口以及它们的实现,使用Spring连接它们。(这里和后面我用了“连接”这个词,英文是wire,意思就是对象A中有对象B的属性,将一个特定的B对象指定给对象A,这也就是IoC容器做的事情)
l业务对象(BO):因为需要Hibernate提供持久化,所以Product和Category这些业务对象需要为他们所有的字段提供getter/setter方法。
l业务服务(Business service):CatalogService接口定义了所有与catalog管理相关的服务:
public interface CatalogService { public Product saveProduct(Product product) throws CatalogException; public void updateProduct(Product product) throws CatalogException; public void deleteProduct(Product product) throws CatalogException; public Product getProduct(String productId) throws CatalogException; public Category getCategory(String categoryId) throws CatalogException; public List getAllProducts() throws CatalogException; public List getAllCategories() throws CatalogException;}
CachedCatalogaServiceImpl是服务接口的实现,它包含了一个CatalogDao对象的setter方法。Spring会 帮助我们完成CachedCatalogServiceImpl和CatalogDao对象的连接。因为我们仅仅是针对接口编码,我们不想与实现紧紧地结 合。
lSpring配置:下面是CatalogService的Spring配置:
<!-- Hibernate Transaction Manager Definition --><bean id="transactionManager"class="org.springframework.orm.hibernate.HibernateTransactionManager"> <property name="sessionFactory"><ref local="sessionFactory"/></property></bean><!-- Cached Catalog Service Definition --><bean id="catalogServiceTarget" class="catalog.model.service.impl.CachedCatalogServiceImpl" init-method="init"> <property name="catalogDao"><ref local="catalogDao"/></property></bean><!-- Transactional proxy for the Catalog Service --><bean id="catalogService"class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"><ref local="transactionManager"/></property> <property name="target"><ref local="catalogServiceTarget"/></property> <property name="transactionAttributes"> <props> <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="save*">PROPAGATION_REQUIRED</prop> <prop key="update*">PROPAGATION_REQUIRED</prop> <prop key="delete*">PROPAGATION_REQUIRED</prop> </props> </property></bean>
Spring声明式的事务管理为CatalogService创造,CatalogService能够与不同的CatalogDao实现相关联。Spring创建和管理一个CatalogService的singleton对象,这里不需要factory。
lSpring和Hibernate的集成:下面是HibernateSessionFactory的配置:
<!-- Hibernate SessionFactory Definition --><bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> <property name="mappingResources"> <list> <value>catalog/model/businessobject/Product.hbm.xml</value> <value>catalog/model/businessobject/Category.hbm.xml</value> <value>catalog/model/businessobject/User.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.cglib.use_reflection_optimizer">true</prop> <prop key="hibernate.cache.provider_class">net.sf.hibernate.cache.HashtableCacheProvider</prop> </props> </property> <property name="dataSource"> <ref bean="dataSource"/> </property>l</bean>
CatalogDao使用HibernateTemplate来结合Hibernate和Spring。HibernateTemplate的配置如下:
<!-- Hibernate Template Defintion --><bean id="hibernateTemplate" class="org.springframework.orm.hibernate.HibernateTemplate"> <property name="sessionFactory"><ref bean="sessionFactory"/></property> <property name="jdbcExceptionTranslator"><ref bean="jdbcExceptionTranslator"/></property> </bean>
集成层
Hibernate使用xml配置文件把BO映射到关系数据库。在JCatalog中,Product.hbm.xml表示 了Product这个业务对象的映射。Category.hbm.xml则是Category这个业务对象的。配置文件和相应的业务对象在相同的目录(当 然这里使用Spring的封装,自己用Hibernate也可以不在同一个目录)。下面是Product.hbm.xml:
<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"><hibernate-mapping package="catalog.model.businessobject"> <class name="Product" table="product"> <id name="id" column="ID" unsaved-value="null"> <generator class="assigned"/> </id> <property name="name" column="NAME" unique="true" not-null="true"/> <property name="price" column="PRICE"/> <property name="width" column="WIDTH"/> <property name="height" column="height"/> <property name="description" column="description"/> <set name="categoryIds" table="product_category" cascade="all"> <key column="PRODUCT_ID"/> <element column="CATEGORY_ID" type="string"/> </set> </class></hibernate-mapping>
Spring将HibernateTemplate连接到CatalogDao上:
<!-- Catalog DAO Definition: Hibernate implementation --><bean id="catalogDao" class="catalog.model.dao.hibernate.CatalogDaoHibernateImpl"> <property name="hibernateTemplate"><ref bean="hibernateTemplate"/></property> </bean>
四,结论
本文向你展示了如何整合JSF,Spring以及Hibernate,并且创建了一个实际的Web应用。这三种技术的联合 提供了一个坚固的Web应用开发框架。Web应用的总体架构应该使用多层结构。JSF是非常适用于MVC设计模式,可以被用来实现表示层。Spring框 架可以被用在业务逻辑层来管理业务对象,提供声明式的事务管理和资源管理。Spring和Hibernate的结合非常的好。Hibernate是一个强 大的O/R mapping框架,能够在集成层提供很好的服务。
由于我们把整个Web应用划分了层次并且针对接口编程,所以每一层使用的技术都可 以被替换掉。比如,Struts可以代替JSF作表示层,JDO可以代替Hibernate在集成层。应用程序层之间的综合并不是可以忽略的,反转控制和 service locator的使用简化了这个工作。JSF提供了其他诸如Struts所欠缺的功能,但这并不是说你应该立刻丢掉Struts开始使用JSF。是否使用 JSF作为你项目的Web框架取决于你项目的状态,功能需求以及你们组的专长。

Leave a comment