我一直认为jsf必定会成为MS的 Net Freamwork的竞争对手。但,jsf尚在发展阶段。至少目前不会。
JSF使用
原文地址:http://www.javaworld.com/javaworld/jw-07-2004/jw-0719-jsf.html
使用JavaServer Faces(JSF)、Spring Framework和Hibernate建立一个真实的Web应用程序
内容概要
使用JSF建立一个真实的Web应用程序不是没有意义的任务,这篇文章介绍了如何将JSF与Sping Framework和Hibernate集成,并且给出了使用这些技术建立这个真实的Web应用程序的最佳实践和设计指导
JavaServer Faces(JSF)技术是J2EE应用程序的一个新的用户接口框架,它非常适合基于MVC(Model-View-Controller)体系结构的应用程序。已经有大量的文章介绍JSF。然而,很多文章都是站在理论研究的层面上,没有挑战一个真实的企业开发。很多问题没有解决,例如,JSF怎样全面适合MVC体系结构?JSF如何与其他JAVA框架集成?业务逻辑应该放在JSF的backing beans里面吗?怎样处理JSF里面的安全问题?最重要的是你怎样使用JSF建立一个真实的Web应用程序?
这篇文章涉及所有这些问题。它向你展示如何集成其他特定的Java框架,Spring Framework和Hibernate,它示范怎样去创建一个叫JCatalog的Web应用程序,一个在线的产品目录系统。这篇文章使用JCatalog例子,介绍了Web应用程序设计的每一个阶段,包括业务需求收集,分析,技术选择,高层体系结构和详细设计。这篇文章论述了JCatalog里面好的和不好的技术,示范了应用程序设计中一些关键方面的方法和步骤。
这篇文章是写给正在从事基于J2EE Web应用程序的Java架构师,开发者,它不是对JSF、Spring Framework和Hibernate的入门教程。如果您对这些领域不熟悉,请参考文章最后的资源链接。
例子应用程序的功能需求
这篇文章的例子应用程序JCatalog是一个真实的Web应用程序,例子足够现实是为了决定应用程序架构而进行意味深长的讨论的基础。我通过介绍JCatalog项目的需求开始。我在这里谈到后面贯穿于整个文章的内容是为了演示技术选择和体系结构设计。
设计Web应用程序的第一步是收集系统的功能需求,这个例子应用程序是一个典型的电子商务应用系统。用户能浏览产品目录和查看产品细节,管理员能管理产品目录。功能还可以增加,举例来说,为了开发一个成熟的电子商务系统,可以添加库存管理和订单处理的功能。
用例
用例分析被用于去访问例子应用程序的功能需求,图1是应用程序的用例图。
图1 用例图
一个用例图确定在一个系统中的参与者以及参与者可以执行的操作。例子应用中7个用例必须被实现。参与者中的User能浏览产品目录和察看产品细节。一旦用户以Administrator身份连接到系统,他就能创建新产品,编辑存在的产品,删除老的产品。
业务规则
JCatalog 必须符合下面的业务规则:
每个产品有一个唯一的产品ID
每个产品至少属于一个目录
产品ID一旦被创建就不能改变
假定
对于产品的设计和实现,我们做下面的假定。
英语是默认语言;不要求国际化
目录中不超过500种产品
目录的更新不频繁
页面流
图2显示了所有JCatalog的页面和它们之间的转换。
图2 页面流图
应用程序有两组页面:公共的国际互联网和管理员的企业内部网。企业内部网只有对那些成功登陆到系统的用户有效。产品概要页面是公用的,它作为产品目录的内容包含在一个HTML框架里面。产品列表是一个特殊的目录,只能被管理员看见,它包含创建、编辑和删除产品的链接。
图3是目录页的一个模型。理想情况下,每一个页面所有的控制和必要的内容明细的模型应该被包含在需求文档里面。
图3 目录页面模型
高级体系结构设计
下一步的设计是Web应用程序的高级体系结构设计,它包括将应用程序细分成功能组件以及将这些组件划分到各自所属的层。高级体系结构设计独立于使用的技术。
多层体系结构
一个多层体系结构将整个系统划分成清晰的单元——客户端、表示层、业务逻辑层、集成层和企业信息系统(EIS),这保证了清晰的责任划分以及可维护性和可扩展性。三层或多层系统已经被证明比没有业务逻辑层的客户-服务器系统具有更多的可升级性和柔韧性。
客户端是数据模型被消费和呈现的地方。对于一个Web应用程序,客户层通常是Web浏览器。基于浏览器的瘦客户不包含表示逻辑;它依赖于表示层。
表示层使用业务逻辑层为用户服务,它知道怎样去处理一个客户请求,怎样去和业务逻辑层结合以及怎样去选择下一个试图去显示。
业务逻辑层包含一个应用程序的业务对象和业务服务。它从表示层接受请求,基于请求处理业务逻辑,作为访问EIS层资源的的中介。业务逻辑层组件使用许多系统级别的服务,例如,安全管理、事物管理和资源管理。
集成层是业务逻辑层和EIS层之间的桥梁。它封装了与EIS层相结合的逻辑。有时,集成层和业务逻辑层的结合是作为中间层被提到。
应用程序数据在EIS层被持久化,包括关系数据库,面向对象数据库和遗留系统。
JCatalog 的体系结构设计
图4显示了JCatalog的高级体系结构设计以及它怎样适合多层体系结构。
|
图4 高级体系结构图
应用程序使用了一个多层的非分布式的体系结构,图4显示应用程序层和每一层技术选择的划分。它也用于应用程序的部署图。对于一个可配置的体系结构,表示层、业务逻辑层和集成层被定位在同样的Web容器。定义良好的接口隔离了每一层的职责。可配置的体系结构使应用程序简单和可升级。
对于表示层,经验告诉我们,最好的实践是选择一个存在的,被验证的Web应用框架,远比设计开发一个定制的框架好。我们有几个Web应用框架可供选择,举例来说,Struts、WebWork和JSF。对于JCatalog项目,我们使用JSF。
对于业务逻辑层,不是使用EJB(Enterprise JavaBeans)就是使用POJO(plain old Java objects)。如果应用程序是分布式的,EJB具有远程接口是一个较好的选择。因为JCatalog是一个典型的没有远程访问请求的Web应用程序,POJO在Spring框架的帮助下,用于实现业务逻辑层。
Pure JDBC(Java Database Connectivity):这是最灵活的实现方法;然而,低级的JDBC和不好的JDBC代码工作是麻烦的,执行的不好。
Entity beans:一个容器管理持久化(CMP,container-managed persistence)的entity bean是隔离数据访问代码和处理O/R(object- relational) mapping数据持久化的昂贵的方法。它是一个以应用服务器为中心的解决办法。一个entity bean不和特定的数据库紧耦合,但是应用程序和EJB容器进耦合。
O/R mapping framework:一个O/R影射的框架采用以对象为中心的方法实现数据持久化。一个以对象为中心的应用程序是容易开发和高度轻便的。在这个领域内存在几个框架——JDO(Java Data Objects),Hibernate,Toplink。CocoBase是一个新的例子。在例子应用程序中我们使用HIbernate。
现在,让我们讨论将应用程序的每一个层联合起来设计的问题。因为JSF相对来说是一个新技术,我强调一下它的使用。
表现层和JavaServer Faces(JSF)
表现层收集用户输入,呈现数据,控制页面导航,代替用户与业务逻辑层交互。表现层也能校验用户输入,维护应用程序的会话状态。下面的章节,我讨论表现层的设计考虑和模式以及我选择JSF去实现JCatalog项目的表现层的原因。
MOdel-View-Controller(MVC)
MVC是Java蓝皮书(BluePrints)中推荐的交互式应用程序体系结构设计模式。MVC分别设计关注的问题,因此减少了代码的重复,集中控制,使应用程序更具扩展性。MVC也帮助开发者使用不同的技术集合,集中他们核心的技术,通过定义清晰的接口进行合作。MVC是表现层的体系结构设计模式。
JavaServer Face
JSF是一个基于Java的Web应用程序服务端的用户接口组件框架。JSF包括表示UI组件和管理其状态的API;处理事件,服务端校验,数据转换;定义页面导航;支持国际化和可访问性;提供所有这些特点的扩展能力。它还包括两个为JSP定制的标签库,一个用于表示JSP页面内的UI组件,一个用于配置服务端的对象组件。
JSF和MVC
JSF很适合基于MVC的表现层体系结构。它提供动作和表现之间清楚地划分。它影响了UI组件和Web层概念,不限定你去使用特定的脚本技术或者标记语言。
JSF backing beans 是model层(后面的章节有关于backing beans 的更多内容)。它们也包含动作,这是控制层的扩展,代理用户对业务逻辑层的请求。请注意,从整体应用程序的体系结构来看,业务逻辑层也能被作为Model层提到。使用JSF定制标签的JSP页面是视图层。Faces Servlet提供控制者的功能。
为什么用JSF
JSF不仅仅只是另一个Web框架,下面是JSF与其他Web框架不同的特点:
象Swing一样面向对象的Web应用程序开发:服务端有状态的UI组件模型,具有事件监听和操作者,开始了面向对象Web应用程序开发。
Backing-bean管理:Backing beans是页面中JavaBeans组件和UI组件的联合。Backing-bean管理UI组件对象定义和对象执行应用程序特殊过程以及控制数据的分离。JSF在正确的范围内执行存储和管理这些backing-bean实例。
可扩展的UI组件模型:JSF UI组件是组成JSF应用程序用户接口可配置、可复用的元素。你能扩展标准的UI组件和开发更多的复杂组件。举例来说,菜单条和树型构件。
灵活的表现模型:一个renderer分隔一个UI组件的功能和视图。多个renderer能被创建,用于定义相同或不同客户端上同样组件的不同外观。
可扩展的转化和校验模型:基于标准的转换者和校验者,你能开发定制的转换者和校验者,它们提供最好的模型保护。
尽管JSF很强大,但是现在还不成熟。组件、转换者和校验者是JSF基本的。每一个校验模型不能处理组件和校验者之间多对多的校验。另外,JSF定制标签不能和JSTL(JSP Standard Tag Library)无缝结合。
在下面的部分,我讨论用JSF实现JCatalog项目时几个关键方面和设计决定。首先讨论JSF中managed beans和backing beans的定义和使用。然后,我介绍JSF中怎样处理安全、分页、缓存、文件上传、校验和错误消息定制。
Managed bean,backing bean,view object和domain object model
JSF介绍了两个新的术语:managed bean 和 backing bean。JSF 提供一个强大的managed-bean工厂。被JSF执行的JavaBean对象管理被叫做managed beans。一个managed bean描述一个bean怎样被创建和管理。它没有利用bean的功能性。
Backing bean 定义页面特性和处理逻辑与UI组件的联合。每一个backing-bean属性被绑定到组件实例或者它的值中的一个。一个backing bean也定义一个组件可执行的功能的集合,例如,校验组件的数据,处理组件触发事件,组件激活时与导航相关的执行过程。
一个典型的JSF应用程序在应用程序的每一个页面中连接一个backing bean。然而,有时在真实的世界里,强迫一个backing bean和一个页面一对一的关系不是一个理想的解决方案。它能造成象代码重复这样的问题。在真实世界的场景里,几个页面可能需要共享在后台的同样的backing bean。例如,在JCatalog项目里,CreateProduct和EditProduct页面共享同样的ProductBean的定义。
一个试图对象是在表示层明确使用的模型对象。它包含了必须显示在视图层的数据和校验用户输入,处理事件和与业务逻辑层相结合的逻辑。backing bean是基于JSF应用程序的视图对象。
在这篇文章中Backing bean 和视图对象是可交换的术语。
比较Struts中的Actionform和Action,在JSF中开发backing beans遵循面向对象设计的最好实践。一个backing bean不仅包含视图数据,也包含与数据相关的行为。在Struts 中,Action 和ActionForm包含数据和逻辑分离。
我们都听说过域对象模型。那么,域对象模型和视图对象有什么不同呢?在一个简单的Web应用程序里,一个域对象模型能被用于所有的层,然而,在一些复杂的Web应用程序里面,一个单独的视图对象模型需要被使用。域对象模型是关于业务对象,应该归入业务逻辑层。它包含特定业务对象相关的业务数据和业务逻辑。一个视图对象包含特定数据和行为的表示。JCatalog项目的ProductListBean提供了一个好的例子。它包含表示层数据和逻辑细节,举例来说,与分页相关的数据和逻辑。从域对象模型分离视图对象的缺点是数据映射必须发生在两个对象模型之间。在JCatalog项目中,ProductBeanBuilder和UserBeanBuilder使用基于反射的Commons BeanUtils去实现数据映射。
安全
当前,JSF没有内置的安全特征。例子的安全需求是基本的:用户连接到管理员使用的公司内部网需要的认证是基于用户名和密码,不需要授权。
在JSF里面几个处理用户认证的方法已经被提出:
Use a base backing bean:这个解决方 案是简单的。然而,它使backing beans与特殊的遗产层次绑定。
Use JSF ViewHandler decorator:这种方法使安全逻辑紧紧地加上一个特殊的Web层技术。
Use a servlet filter:一个JSF 应用程序与其他基于Java的Web应用程序不同。它在一个恰当的地方使用一个过滤器处理认证检查。这种方法使Web应用程序中的认证逻辑减弱了。
在例子应用程序中,SecurityFilter类处理用户认证。当前,受保护的资源只有三个页面,为了简单,它们被硬编码在Filter类里面。可以通过扩展安全规则来改进它,把受保护的资源放到配置文件中。
分页
应用程序的目录页需要分页。表现层能处理分页,这意味着所有数据必须被重新得到存储在这层。分页也能在业务逻辑层、集成层甚至是EIS层处理。JCatalog项目的假定是在目录中的产品不超过500种。所有产品信息适合保存在用户session中。分页逻辑存在ProductListBean类中。与分页有关的参数“每页的产品”通过JSF的managed-bean工具配置。
缓存
缓存是Web应用程序中改善性能的众多重要技术中的一种。缓存能在应用程序体系结构内的许多层完成。体系结构中的一层减少调用它下面的层时,缓存是非常有益的。JSF managed-bean工具使在表现层实现缓存更容易。通过改变一个managed bean的范围,包含在managed bean中的数据能在不同的范围内缓存。
例子应用程序使用二级缓存。第一级缓存在业务逻辑层里面。CachedCatalogServiceImpl类维护所有产品和目录的读/写缓存。Spring 管理的类作为一个单独服务bean。所以,一级缓存是一个应用程序范围的读/写缓存。
对于简单的分页逻辑和将来应用程序速度的提高,表现层的会话范围内的产品也被缓存。每个用户维护他session里面自己的ProductListBean。缺点是占用系统内存和存在旧数据。在一个用户session的持续时间里,如果管理员更新了目录,用户可能会看到旧的目录数据。然而,基于这样的假定,目录中部超过500种产品,而且目录更新不频繁,我们应该能够忍受这些缺点。
文件上传
目前,JSF的Sun参考实现中不支持文件上传。Struts有很好的文件上传能力,然而,Struts外观集成库是必须使用。 在JCatalog项目中,一个图片与每个产品关联。一个用户创建一个新的产品之后,必须上传与之相关的图片。图片保存在应用服务器的文件系统里面。产品ID是图片名.
例子应用程序使用<input type="file">,Servlet 和Jakarta通用的文件上传API,实现一个简单的文件长传功能。这个功能使用两个参数:产品图片路径和图片上传结果页面。它们都通过ApplicatonBean配置。请参考FileUploadServlet类的详细资料。
校验
JSF具有的标准校验是简单基本的,不能满足真实的需求。开发自己的JSF校验是容易的。我在例子应用程序中使用定制标签来开发SelectedItemsRange校验。它校验通过UISelectMany UI组件选择的项目数量:
<h:selectManyListbox value="#{productBean.selectedCategoryIds}" id="selectedCategoryIds">
<catalog:validateSelectedItemsRange minNum="1"/>
<f:selectItems value="#{applicationBean.categorySelectItems}" id="categories"/>
</h:selectManyListbox>
更多的详细资料请参考例子应用程序。
错误消息定制
在JSF里面,你可以设置资源包定制转换和校验时的错误消息。资源包被设置在faces-config.xml里面:
<message-bundle>catalog.view.bundle.Messages</message-bundle>
The error message's key-value pairs are added to the Message.properties file:
#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 Framework
业务对象和业务服务在业务逻辑层。一个业务对象不仅包含数据,也有与特定对象关联的逻辑。在例子应用程序中标识了三个业务对象:Product,Category和User.
业务服务与业务对象相结合,提供高级的业务逻辑。一个包含直接使用的服务接口的标准的业务接口层应该被定义。在Spring Framework的帮助下,POJO实现了Jcatalog项目业务逻辑层。有两个业务服务:CatalogService包含与目录管理相关的业务逻辑,UserService包含了用户管理逻辑。
Spring是基于控制反转概念(IOC,inversion of control)。在例子应用程序中使用的Spring特征包括:
Bean management with application contexts:Spring能有效地组织我们的中间层对象,垂直处理。Spring 能避免一个实体功能的分解,促进好的面向对象设计实现,举例来说,接口设计。
Declarative transaction management:Spring 使用AOP(aspect-oriented programming)去描述不使用EJB容器的声明性事务处理。这种方法,事务管理能被应用到任何POJO。Spring事务管理不和JTA(Java Transaction API)绑定,使用不同的事物策略工作。在例子应用程序中声明性事务管理Hibernate中的事务。
Data-access exception hierarchy:Spring提供一个值得回味的异常层次代替SQLException。使用Spring数据访问异常层次,Spring数据访问异常翻译者必须在Spring配置文件里面定义:
<bean id="jdbcExceptionTranslator"
class= "org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
在例子应用程序中,如果插入的一个新产品的ID是重复的,一个 DataIntegrityViolationException 会被抛出。这个异常会被捕获然后作为DuplicateProductIdException被抛出。这种方法,DuplicateProductIdException能处理不同的数据访问异常。
Hibernate integration:Spring不强迫我们使用它强大的JDBC抽象特征。它和O/R映射框架集成的很好,尤其是Hibernate。Sping提供有效的、安全的Hibernate会话操作。在应用程序上下文操作Hibernate的配置SessionFactories 和JDBC数据源,使应用程序容易测试。
集成层和HIbernate
Hibernate是一个开源O/R映射框架,它减少使用JDBC API的需要。Hibernate支持所有主流的SQL数据库管理系统。Hibernate Query Language是SQL面向对象的最小的扩展来设计的,在对象和关系世界间提供了一个优雅的桥。Hibernate提供数据恢复和更新的工具,事务管理,数据连接池,programmatic and declarative queries,声明实体关系管理。
Hibernate和其他O/R映射框架相比入侵性较小。使用反射和运行时产生字节码,SQL在系统开始时产生。它允许我们开发符合Java习惯的持久对象,包括联合,继承,多态,聚合和Java集合框架。在例子应用程序中的业务对象是POJO,不需要实现Hibernate的特殊接口。
Data Access Object (DAO)
JCatalog项目使用DAO模式。这个模式抽象和封装了所有对数据源的访问。应用程序有两个DAO接口。CatalogDao和UserDao。它们的实现类是HibernateCatalogdaoImpl和HibernateUserDaoImpl包含与Hibernate相关的管理和持久化数据逻辑。
实现设计
现在,让我把每件事情都串起来,实现JCatalog项目。你可以冲资源列表中下载应用程序的完整源代码。
数据库设计
我们为例子应用程序创建指定目录的结构,它包含4个表,如图5:
|
图5 数据结构图
类设计
图6图解了JCatalog项目的类图
图6 类图
面向接口编程贯穿于整个设计。在表现层,四个bean被使用:ProductBean, ProductListBean, UserBean和 MessageBean。业务逻辑层包含两个服务(CatalogService and UserService)和三个业务对象(Product, Category, and User)。集成层包括两个DAO接口和它们的Hibernate实现。Spring application contexts 包含和管理业务逻辑层和集成层的很多object beans。ServiceLocator使JSF和业务逻辑层结合到一起。
Wire everything up
因为这篇文章篇幅的限制,我们只看一个用例。CreateProduct用例示范了怎样将每件事情串起来建造应用程序。深入细节以前,让我们使用一个序列图(图7)示范所有层端到端的整合:
图7 CreateProduct用例的序列图
现在,让我们通过对每一层的介绍讨论如何实现CreateProduct用例的更多细节。
表现层
表现层的实现包括创建JSP页面,定义页面导航,创建和配置backing beans,将JSF与业务逻辑层结合。
JSP page:createProduct.jsp是创建新产品的页面。它包括UI组件和捆绑这些组件的ProductBean。ValidateItemsRange自定义标签检验用户选择目录的数目。每个新产品至少有一个目录被选择。
Page navigation:应用程序的导航定义在应用程序的配置文件里面,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>
Backing bean: ProductBean不仅包含了页面中UI组件与数据映射的属性,也包含三个actions: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里面,基于ProductBean的一个Product业务对象被建立。ServiceLocator查询CatalogService。最后,createProduct的请求被委派给业务逻辑层的CatalogService。
Managed-bean declaration: 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有一个请求的范围,这意味着如果ProductBean在JSP页面内引用JSF执行为每一个请求创建ProductBean实例的任务。被管理的ID属性与productId这个请求参数组装。JSF从请求得到参数,设置managed property。
Integration between presentation and business-logic tiers: ServiceLocator抽象了查询服务的逻辑。在例子应用程序中,ServiceLocator被定义成一个一个接口。接口被JSF managed bean实现为ServiceLocatorBean,它从Spring application context查询服务:
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中的一个属性。JSF managed bean容易连接ServiceLocator执行必须访问ServiceLocator的那些managed beans。使用了Inversion of control(IOC,控制反转)
业务逻辑层
定义业务对象,创建服务接口和实现,在Spring中配置这些对象组成了这一层的任务。
Business objects: 因为Hibernate提供了持久化,Product和Category业务对象需要为它们包含的所有属性提供getter和setter方法。
Business services:CatalogService接口定义了所有与目录管理有关的服务:
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;
}
CachedCatalogServiceImpl服务的接口实现,它包含CatalogDao对象的一个setter。Spring将CachedCatalogServiceImpl 和CatalogDao连接在一起。因为我们提供了接口,所以对实现的依赖不是很紧密。
Spring configuration: 下面是CatalogService的Spring comfiguration:
<!-- 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,不需要工厂。
现在,业务逻辑层准备好了,让我们将它与集成层整合。
Integration between Spring and 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>
</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配置文件去映射业务对象到关系型数据库。在JCatalog项目中,Product.hbm.xml表示Product业务对象的映射。Category.hbm.xml用于业务对象Category。配置文件和相应的业务对象在同样的目录下。下面是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>
CatalogDao通过Spring使用HibernateTemplate连接:
<!-- Catalog DAO Definition: Hibernate implementation -->
<bean id="catalogDao" class="catalog.model.dao.hibernate.CatalogDaoHibernateImpl">
<property name="hibernateTemplate"><ref bean="hibernateTemplate"/></property>
</bean>
结论
这篇文章介绍了怎样将JSF集成到Spring Framework和Hibernate,建立了一个真实的应用程序。这三种技术的联合提供了一个可靠的Web应用程序开发框架。一个多层体系结构应该做为Web应用程序的高级体系结构。JSF很适合MVC设计模式,能够被用于实现表示层。Spring框架能被用于业务逻辑层去管理业务对象,提供声明性事务管理和资源管理。Spring与Hibernate结合的很好。Hibernate是一个强有力的O/R映射框架,能够提供集成层的服务。
通过将Web应用程序划分成不同的层和面向接口编程,每一层的技术可以被取代。例如, 在表示层Struts能取代JSF,在集成层JDO能取代Hibernate。应用程序层之间的整合不是没有意义的,使用inversion of control和Service Locator设计模式能使这个工作容易。JSF提供了其他框架,如Struts所缺少的功能。然而,这不意味着你应该立刻抛弃Struts而开始使用JSF 。无论怎样,你的项目是否使用JSF作为你的Web框架,取决于你项目的状态和功能需求以及团队专家的意见。
Resources
Download the JCatalog project sample application:
http://www.javaworld.com/javaworld/jw-07-2004/jsf/jw-0719-jsf.zip
Official JavaServer Faces site:
http://java.sun.com/j2ee/javaserverfaces/index.jsp
A good JSF tutorial can be found in The J2EE 1.4 Tutorial (Chapters 17 to 21):
http://java.sun.com/j2ee/1.4/docs/tutorial/doc/index.html
More articles and books on JSF:
http://jsfcentral.com/reading/index.html
Official Spring Framework site:
http://www.springframework.org
Good introduction to the Spring Framework by Rod Johnson:
http://www.theserverside.com/articles/article.tss?l=SpringFramework
Rod Johnson's book Expert One-on-One J2EE Design and Development (Wrox, October 2002; ISBN: 0764543857) is the corner stone of the Spring Framework:
http://www.wrox.com/WileyCDA/WroxTitle/productCd-0764543857.html
Official Hibernate site:
http://www.hibernate.org
Online documentation of Hibernate:
http://www.hibernate.org/hib_docs/reference/en/html/
Introduction to the integration between the Spring Framework and Hibernate:
http://hibernate.bluemars.net/110.html
"Designing Enterprise Applications with the J2EE Platform, Second Edition" is a good introduction to the multitiered architecture and MVC design pattern:
http://java.sun.com/blueprints/guidelines/designing_enterprise_applications_2e/index.html
Commons BeanUtils:
http://jakarta.apache.org/commons/beanutils/
Commons FileUpload:
http://jakarta.apache.org/commons/fileupload/
For more on JavaServer Faces, read the following JavaWorld articles by David Geary:
"A First Look at JavaServer Faces, Part 1" (November 2002)
"A First Look at JavaServer Faces, Part 2" (December 2002)
"JavaServer Faces, Redux" (November 2003)
Browse the JavaServer Pages section of JavaWorld's Topical Index:
http://www.javaworld.com/channel_content/jw-jsp-index.shtml
Browse the Enterprise Java section of JavaWorld's Topical Index:
http://www.javaworld.com/channel_content/jw-enterprise-index.shtml