??xml version="1.0" encoding="utf-8" standalone="yes"?> 探烦 JavaServer Faces 框架中用的设计模式 U别: 中
Anand Prakash Joshi
(ananjosh@in.ibm.com), 软g工程? IBM 2006 q?1 ?04 ?/p>
设计模式可以帮助用户在更高层ơ上抽象l节Q更好地理解体系l构。如果比较熟(zhn)?GoF 设计模式?JavaServer Faces (JSF) 框架Q本文可以帮助?zhn)z察 JSF 框架中用的设计模式Q深入理解其工作原理?/p>
本文探讨?JSF 框架中用的设计模式。详l讨论的设计模式包括 Singleton、Model-View-Controller、Factory Method、State、Composite、Decorator、Strategy、Template Method ?Observer 模式?
设计模式?JavaServer Faces (JSF) 技?/span>
首先要地介绍一下模式和 JSF 框架?/p>
现在我们来讨?JSF 体系l构中的各种设计模式。本文将详细讨论 Singleton、Model-View-Controller、Factory Method、State、Composite、Decorator、Strategy、Template Method ?Observer 设计模式。我分析每U模式的用途及(qing)其在 JSF 框架中的作用?/p>
Singleton 模式的目的是保证cd有一个实例被加蝲Q该实例提供一个全局讉K炏V当启动h JSF 支持?Web 应用E序ӞW(xu)eb 容器初始化一?FacesServlet 实例。在q个阶段QFacesServlet Ҏ(gu)?Web 应用E序实例?Application ?LifeCycle 实例一ơ。这些实例就采用众所周知?Singleton 模式Q通常只需要该cd的一个实例?/p>
使用 JSF ?Web 应用E序只需?Application ?LifeCycle cȝ一个实例。LifeCycle 理多个 JSF h的整个生命期。因为其状态和行ؓ(f)在所有请求之间共享,q些对象采用 Singleton 模式合情合理。LifeCycle l护?PhaseListeners 也是 Singleton 模式的。PhaseListeners 由所?JSF h׃n。在 JSF 框架中可以广泛?Singleton 模式Q以减少内存占用和提供对象的全局讉K。NavigationHandlerQ用于确定请求的逻辑l果Q和 ViewHandlerQ用于创图)也是使用 Singleton 模式的例子?/p>
MVC 模式的目的是从数据表C(ViewQ中数据(?ModelQ分d来。如果应用程序有多种表示Q可以仅替换视图层而重用控制器和模型代码。类似的Q如果需要改变模型,可以在很大程度上不改变视囑ֱ。控制器处理用户动作Q用户动作可能造成模型改变和视图更新。当用户h一?JSF 面Ӟh发送到 FacesServlet。FacesServlet ?JSF 使用的前端控制器 servlet。和其他很多 Web 应用E序框架一PJSF 使用 MVS 模式消除视图和模型之间的耦合。ؓ(f)了集中处理用戯求,控制?servlet 改变模型q将用户D到视图?/p>
FacesServlet ?JSF 框架中所有用戯求都要经q的控制器元素。FacesServlet 分析用户hQ用托?bean Ҏ(gu)型调用各U动作。后収ͼbackingQ或托管QmanagedQbean 是该模型的例子。JSF 用户界面QUIQ组件是视图层的例子。MVC 模式把Q务分解给h不同技能的开发h员,使这些Q务能够同时进行,q样 GUI 设计人员可以用丰富的 UI lg创徏 JSF 面Q同时后端开发h员可以创建托?bean 来编写专门的业务逻辑代码?/p>
Factory Method 模式的目的是定义一个用于创建对象的接口Q但是把对象实例化推q到子类中。在 JSF 体系l构中,Factory Method 模式被用于创建对象。LifeCycleFactory 是一个创建和q回 LifeCycle 实例的工厂对象。LifeCycleFactory ?getLifeCycle (String LifeCycleId) Ҏ(gu)采用 Factory Method 模式Q根?LifeCycleId 创徏Q如果需要)q返?LifeCycle 实例。自定义?JSF 实现可以重新定义 getLifeCycle 抽象Ҏ(gu)来创定义?LifeCycle 实例。默认的 JSF 实现提供默认?LifeCycle 实例。此外,对于每个 JSF hQFacesServlet 都从 FacesContextFactory 得到 FacesContext。FacesContextFactory 是一个抽象类Q公开?getFacesContext APIQJSF 实现提供?FacesContextFactory ?getFacesContext API 的具体实现。这是另外一个?Factory Method 模式的例子,具体?FacesContextFactory 实现创徏 FacesContext 对象?/p>
State 模式的目的是在表C状态的不同cM间分配与状态有关的逻辑。FacesServlet ?LifCycle 实例调用 execute ?render Ҏ(gu)。LifeCycle 协调不同?Phrase 以便执行 JSF h。在q里 JSF 实现遵循了 State 模式。如果没有用这U模式,LifeCycle 实现׃(x)被大量的条gQ即 “if?语句Q搅得一塌糊涂。JSF 实现为每个状态(或阶D)创徏单独的类q调?step。phase 是一个抽象类Q定了每?step 的公共接口。在 JSF 框架中定义了六个 phraseQ即 stepQ:(x)RestoreViewPhase、ApplyRequestValues、ProcessValidationsPhase、UpdateModelValuesPhase、InvokeApplicationPhase ?RenderResponsePhase?/p>
?State 模式中,LifeCycle ?FacesContext 对象传递给 phase。每个阶D|状态改变传递给它的上下文信息,然后讄 FacesContext 本n中的标志表明下一个可能的步骤。JSF 实现在每个步骤中改变其行为。每个阶D都可以作ؓ(f)下一个阶D늚起因。FacesContext 有两U标?renderResponse ?responseComplete 可以改变执行的顺序。每个步骤执行完成后QLifeCycle (g)查上一阶段是否讄了这些标志。如果设|了 responseCompleteQLifeCycle 则完全放弃请求的执行。如果经q某个阶D后讄?renderResponse 标志QJSF ׃(x)跌剩下的阶D而直接进?Render Response 阶段。如果这两个标志都没有设|,LifeCycle ׃(x)按顺序l执行下一步?/p>
Composite 模式让客户代码能够统一处理复合对象和基本对象。复合对象是基本对象的容器。在W一阶段QRestore View 阶段Q和最后一个阶D(Render Response 阶段Q,使用 JSF UI lg构?UI View。UIComponentBase 是 Composite 模式?Component 抽象cȝ一个例子。UIViewRoot ?Composite c,?UIOutputQ比方说Q就是叶子(或者基本类Q。UIComponentBase cd义了叶子和复合对象的公共Ҏ(gu)Q如~码/解码值和子节点管理函数。子节点理函数Q如 getChildrenQ对于叶子节点返回空列表Q对于复合节点则q回其子节点?/p>
Decorator 模式的目的是不通过子类化动态扩展对象的行ؓ(f)。JSF 框架有很多扩展点Q即可插入机Ӟ。JSF 实现可?Decorator 模式替换默认?PropertyResolver、VariableResolver、ActionListener、NavigationHandler、ViewHandler ?StateManager。通常自定义实现接受通过构造函C递给它的默认实现的引用。自定义实现仅仅改写功能的一个子集,而将其他功能委托l默认实现。如果希望实现自定义?ViewHandlerQ改写默?ViewHandler 实现?calculateLocale Ҏ(gu)Q可以像 清单 1 那样~写 Strategy 模式的目的是装不同的概c(din)JSF 框架采用 Strategy 模式使用委托实现模型呈现 UI lg。JSF 技术支持两U呈现模型。在直接实现模型中,UI lgҎ(gu)到的h中的数据q行解码Q然后编码这些数据进行显C。在委托实现模型中,解码和编码操作委托给和组建关联的专门呈现器。后一U模型利用了 Strategy 设计模式Q比直接实现更灵zR在 Strategy 模式中,不同的法装在单独的对象中,从而可以动态地改变法。JSF 实现可以用已有的 renderkit 实例注册另外的呈现器Q当应用E序启动的时候,JSF 实现d配置文g这些呈现器?UI lg联系在一赗?/p>
Template Method 模式的目的是变化的步骤推迟到子cMQ而在父类中定义那些固定的法步骤。JSF 框架通过 PhraseListeners 展现?Template Method 模式提供的功能。采?Template MethodQ或?“hook”)使得 Web 作者可以ؓ(f)不同阶段之间的可选步骤提供实玎ͼ而主要阶D仍然和 JSF 框架的定义一致。JSF 框架提供?PhaseListenersQ概念上cM?Template Method 模式中的可变步骤。JSF 框架有六个预定义的阶D,在每个阶D之_(d)W(xu)eb 作者可以实?PhaseListeners 来提供类g Template Method hook ?hook。事实上Q这U结构比 Template Method 模式更具有扩展性。可以通过注册 PhraseId ?ANY_PHRASE ?PhaseListener 在每个阶D后提供 hook。如?PhaseId ?ANY_PHASEQJSF 实现׃(x)在每个阶D之前和之后调用?PhaseListener。JSF 框架中的实现略有不同Q因为可以根本没?PhaseListenerQ但是在 Template Method 模式中,子类通常重新定义父类中抽象的可变步骤?/p>
Observer 模式的目的是当目标对象的状态改变时自动通知所有依赖的对象Q即观察器)。JSF ?UI lg中实C Observer 模式。JSF 有两cdZӞ(x)ActionEvent ?ValueChangedEvent。ActionEvent 用于定用户界面lgQ如按钮Q的ȀzR当用户单击按钮ӞJSF 实现通知d到该按钮上的一个或多个动作监听E序。于是该按钮被激z,或者说按钮Q主体)的状态改变了。添加到按钮上的所有监听程序(卌察器Q都收到通知该主体状态已l改变。类似的Q当输入 UI lg中的值改变时QJSF 实现通知 ValueChangeListener?/p>
JSF 框架利用?Singleton、Model-View-Controller、Factory Method、State、Composite、Decorator、Strategy、Template Method ?Observer 设计模式。因为它的体pȝ构徏立在已经验证的设计模式的基础上,q是一个健壮的框架Q模式在 JSF 框架中得C很好的利用?
Anand 是一?Sun 认证的企业架构师Q几q来一直研I?Web 技术。他?WebSphere 理控制台应用程序的设计和开发做了多斚w的A(ch)献。Anand 曄?IBM 国工作q几q_(d)目前?IBM 印度工作?/p>
<!-- Hibernate SessionFactory Definition -->
<!-- Hibernate Template Defintion -->
<?xml version="1.0"?>
<!-- Catalog DAO Definition: Hibernate implementation -->
使用 JSF 架构q行设计
拓展 Tomcat 应用
本文中,作?Anand Joshi 使用 JSF 框架中的设计模式阐释?JavaServer Faces (JSF) 体系l构。他讨论?JSF 体系l构中用的 GoF 设计模式Q以?qing)这些模式?JSF 框架中的作用。Q何对设计模式?JSF 体系l构有一定了解的人都能从 Anand 详细的介l中有所收获?读者应该对 GoF 设计模式?JSF 技术有很好的了解?/blockquote>
CustomViewHandler
c:(x)
清单 1. CustomViewHandler 片段
public class CustomViewHandler extends ViewHandler {
public CustomViewHandler(ViewHandler handler) {
super();
oldViewHandler = handler;
}
private ViewHandler oldViewHandler = null;
public void renderView (facesContext context, UIViewRoot view) {
//delegate method to oldViewHandler
oldViewHandler.renderView(context, view);
}
//custom implementation of calculateLocale
public Locale calculateLocale(FacesContext context) {
}
}
学习(fn)
获得产品和技?/b>
讨论
本文涉?qing)到上面的这些问题,它将演示如何JSF、Spring和Hibernate整合在一P构架Z个名为JCatalog的在U品h(hun)目系l?/font>
。利用该DemoQ本文涵盖了Web应用开发的每一个阶D,包括需求收集、分析,技术选择Q系l架构和实现。本文讨Z在JCatalog中涉?qing)到的各U技术的优点和缺点ƈ展示了一些关键部分的设计Ҏ(gu)?br />
本文的对象是从事ZJ2ee的Web应用架构人员和开发h员,它ƈ不是对JSF、SpringFramework和Hibernate的简单介l,如果对这些领域不甚了解,请参看相兌源?br />
该范例的功能需?/b>
JCatalog是一个现实世界的Web应用Q我首先描述JCatalog的需求,在通篇的技术决{和架构设计旉涉?qing)到本部分?br />
在设计Web应用的第一阶段是收集系l的功能需求,范例应用是一个典型的?sh)子商务应用pȝQ用户可以浏览品的catalogq查看品的详细情况Q而管理员可以理产品的catalog。通过增加一些其他功能,如inventory理和订单处理等Q该应用可成Z个成熟的?sh)子商务pȝ?br />
Use cases
Use-case分析被用来展C例应用的功能需求,?是该应用的use-case图?br />
use-case囄于表C系l中的actors以及(qing)可能q行的operationsQ在该应用中有七个use-caseQ用戯够浏览?catalog和查看品的详细情况Q一旦用L(fng)录到pȝ中,她将成ؓ(f)理员,从而可以创建新的品,~辑已存在的产品或者删除老的产品{?br />
Business rules
JCatalog必须W合以下business rules:
Assumptions
我们在系l的设计和实C做以下假定:(x)
Page flow
?昄了所有的JCatalog的pages以及(qing)它们之间的transitions关系Q?br />
该应用中存在两组pagesQ公开的internet和用于管理的intranetQ其中intranet只能被那些成功登录到pȝ的用戯问?ProductSummary不作Z个单独的page展示l用P它显C在Catalog page中的frame中。ProductList只对理员可视,它包含用于创建、编辑和删除产品的链接?br />
?是一个Catalog面的示意图Q理想状况下Q在需求文档中应该包含每一늚详细C意图?br />
构架设计
Web应用开发的下一个阶D|构架设计Q它包括应用划分ؓ(f)多个功能lgq将q些lg分割l合成层Q高层的构架设计应该中立于所选用的特定技术?br />
多层架构
多层架构是将整个pȝ清晰的分为多个功能单元:(x)client、presentation、business-logic、integration?EISQ这确保职责得到清晰的划分Q得系l更易于l护和扩展。具有三层或{多层的pȝ被证明比C/S模型h更好的~性和灉|性?br />
client层是使用和表C数据模型的地方Q对于一个Web应用Qclient层通常是浏览器Q基于浏览器的瘦客户端不包含M表示逻辑Q它依赖?/font>
presentation
层?br />
presentation层将business-logic层的服务展示l用P它应知道如何处理用户的请求,如何同business-logic层交互,q且知道如何选择下一个视图显C给用户?br />
business-logic层包含应用的business objects和business services。它接受来在于presentation层的h、基于请求处理业务逻辑。业务逻辑层组件将受益于系l的服务,如安全管理、事务管理和资源理{?br />
integration层是介于
business-logic
层和EIS层之间的桥梁Q它?yu)装了与EIS层交互的逻辑。有Ӟintegration层和business-logic层合UCؓ(f)中间层?br />
应用的数据被保存在EIS层中Q它包括关系数据库、面向对象数据库和以?qing)遗留系l等?br />
JCatalog的构架设?/font>
?昄了JCatalog的构架设计以?qing)如何应用于多层构架pȝ中?br />
该应用采用了多层非分布式的构Ӟ?展示了系l的分层以及(qing)每一层中选择的技术,它同时又是该范例的部|图Q它的presentation?business-logic和integration层将存在于同一个web容器中。定义良好的接口孤立每一层的职责Q这一架构使得应用更ؓ(f)单和更好的~性?br />
对于presentation层,l验表明Q最好的Ҏ(gu)是选择已存在的q已得到证明了的Web应用框架Q而不是自己去设计和开发新的框架。我们拥有多个可选择的框Ӟ如StrutsQW(xu)ebWork和JSF{,在JCatalog中,我们选择采用JSF?br />
EJB和POJO都可以用来创Z务逻辑层,如果应用是分布式的,采用hremote接口的EJB是一个好的选择Q由于JCatalog是一个典型的不需要远E访问的Web应用Q因此选用POJOQƈ充分利用Spring Framework的帮助,是实现业务逻辑层的更好选择?br />
integration层利用关pd数据库事先数据的持箋化,存在多种Ҏ(gu)可用来实玎ͼ(x)
现在Q我们将讨论每一层中的设计问题,׃JSF是一个相对较新的技术,因此着重于它的使用Q?br />
presentation
层和JSF
表示层的功能是收集用L(fng)输入、展C数据、控刉面导航ƈ用L(fng)输入传递给业务逻辑层,表示层同旉要验证用L(fng)输入以及(qing)l护应用的session状态。在下面几部分中Q我讨C层设计时的考虑和模式,q说明选择JSF作ؓ(f)JCatalog表示层的原因?br />
MVC
MVC是Java-Blueprints推荐的架构设计模式,MVC几个方面分d来,从而减代码的重复Q它以控制ؓ(f)中心q得应用更h展性。MVC同时可帮助具有不同技能的用户更关注于自己的技能,通过定义良好的接口进行相互合作。MVC是表C层的架构设计模式?br />
JSF
JSF是Web应用的服务器端用L(fng)件框Ӟ它包含以下APIQ表CUIlg、管理它们的状态、处理事件、服务器端验证、数据{换、定义页面导航、支持国际化Qƈ些特性提供扩展能力。它同时包括两个JSP的tag库以在JSP面中表CUIlgQ以?qing)将lgwire为服务器端对象?br />
JSF和MVC
JSF非常适合于基于MVC的表C层架构Q它在行为和表示之间提供了清晰的分离Q它使得你可以采用熟(zhn)的UIlg和web层概念而无需受限于某U特D的脚本技术或标记语言?br />
JSF backing beans是JSF的Model层,此外Q它同样包含actionsQaction是controller层的扩展Q用于将用户的请求委z业务逻辑层。这里请注意Q从整体的应用构架看Q业务逻辑层也被称为model层。包含JSF标签的JSP面是表C层QFaces Servlet提供了controller的功能?br />
Z么选用JSFQ?/b>
JSF不仅仅是另外一个Web框架Q下面这些特性是JSF区别于其他Web框架之所在:(x)
管如此QJSF目前未成熟Q随同JSF发布?components、converters和validators都是最基础的,而且per-component验证模型不能处理components 和validators间的many-to-many验证。此外,JSF标签不能与JSTL间无~的整合在一赗?br />
在下面的章节中,我将讨论几个在JCatalog实现中的关键部分和设计决{。我首先解释managed bean的定义和使用以及(qing)JSF中的backing beanQ然后,我将说明如何处理安全、分c(din)caching、file upload、验证以?qing)错误信息定制?br />
Managed bean,backing bean,view object 和domain object model
JSF中引入了两个新的名词Qmanaged bean和backing bean。JSF提供了一个强大的managed-bean工具Q由JSF来管理的JavaBean对象UCؓ(f)managed-beanQ一?managed bean表述了一个bean如何被创建和理Q它不包含该bean的Q何功能性描q?br />
backing bean定义了与面中用的UIlg相关联的属性和处理逻辑。每一个backing-bean属性邦定于一个组件实例或某实例的value。一?backing-bean同时定义了一l执行组件功能的Ҏ(gu)Q例如验证组件的数据、处理组件触发的事g、实施与lg相关的导航等?br />
一个典型的JSF应用其中的每个面和一个backing-beanl合hQ然而在现实应用中,强制的执行这Uone-on-one的关pM是一U理想的解决Ҏ(gu)Q它可能?x)导致代码重复等问题。在现实的应用中Q多个页面可以共享一个backing-beanQ例如在JCatalog中, CreateProduct和EditProduct共享同一个ProductBean定义?br />
model对象特定于表C层中的一个view对象Q它包含必须昄在view层的数据以及(qing)验证用户输入、处理事件和与业务逻辑层交互的处理逻辑{。在Z JSF的应用中backing bean是view对象Q在本文中backing bean和view对象是可互换的名词?br />
Ҏ(gu)于struts中的ActionForm和ActionQ利用JSF中的backing-beanq行开发将能更好的遵@面向对象Ҏ(gu)Q一?backing-bean不仅包含view数据Q而且q包含与q些数据相关的行为,而在struts中,Action和ActionForm分别包含数据和逻辑?br />
我们都应该听说过domain object modelQ那么,domain object model和view对象之间有什么区别呢Q在一个简单的Web应用中,一个domain object model能够横穿所有层中,而在复杂的应用中Q需要用C个单独的view对象模型。domain object model应该属于业务逻辑层,它包含业务数据和与特定业务对象相关的业务逻辑Q一个view对象包含presentation-specific的数据和逻辑。将view对象从domain object model中分d来的~点是在q两个对象模型之间必出现数据映。在JCatalog中,ProductBeanBuilder?UserBeanBuilder利用reflection-based Commons BeanUtils来实现数据映?br />
安全
目前QJSF没有内徏的安全特性,而对于范例应用来说安全需求是非常基础的:(x)用户d到administration intranet中仅需用户名和密码认证Q而无需考虑授权?br />针对于JSF的认证,已有几种Ҏ(gu)提出Q?br />
在我们的范例E序中,SecurityFilterc被用来处理用户的认证,目前Q受保护的资源只包含三个面Q出于简单的考虑Q将它们的位|被编码到FiltercM?br />
分页
该应用中的Catalog面需要分,表示层可用来处理分页Q即它取出所有的数据q保存在q一层;分页同样可在business-logic层?integration层、甚至EIS层中实现。由于在JCatalog中假设不过500个品,因此所有的产品信息能存攑֜一个user session中,我们分逻辑攑֜了ProductListBean中,与分늛关的参数通过JSF managed-bean工具配置?br />
Caching
Caching是提高Web应用性能的最重要技术之一Q在应用构徏中的很多层中都可以实现caching。JSF managed-bean工具可以使在表示层实现caching非常Ҏ(gu)。通过改变一个managed bean的范_(d)q个managed bean中包含的数据可以在不同的范围内缓存?br />
范例应用中采用了两cachingQ第一Ucaching存在于业务逻辑层,CachedCatalogServiceImplcȝ护了一个所有品和目录的读写cacheQSpring该cMZ个singleton service bean来管理,所以,一Ucache是一个应用范围的dcache?br />
Z化分逻辑q进而提高应用的速度Q品同样在session范围内缓存到表示层,每一个用L(fng)护着他自qProductListBeanQ这一Ҏ(gu)的缺Ҏ(gu)内存的消耗和数据的失效问题,在一个用户session中,如果理员更改了catalogQ用户可到的是失效的数据,然而,׃我们假设应用的数据不?x)经常的改变Q所以这些缺点将能够忍受?br />
File upload
目前的JSF Sun参考实C不支持file upload。Struts虽已h非常不错的file upload能力Q然而要想用这一Ҏ(gu)需要Struts-Faces整合库。在JCatalog中,一个图像与一个品相兌Q在一个用户创Z新的产品后,她必d相应的图片上传,囄保存在应用服务器的文gpȝ里,产品的ID是囑փ名称?br />
范例应用中采?input type="file" />、Servlet和Jakarta Common的file-upload API来实现简单的文g上传功能Q该Ҏ(gu)包含两个参数Q图像\径和囑փ上传l果面。它们将通过ApplicationBean来配|,详细内容请参?FileUploadServletcR?br />
Validation
JSF中发布的标准validator是非常基的,无法满现实的需要,但很Ҏ(gu)开发出自己的JSF validatorQ在范例中,我开发了SelectedItemsRange validatorQ它用来验证UISelectManylg中选择的数量:(x)
详细情况请参看范例?br />
定制错误信息
在JSF中,你可以ؓ(f)converters和validators创徏resource bundle和定刉误信息,一个resource bundle可在faces-config.xml中创建:(x)
q将错误信息的key-value对加到Message.properties文g中:(x)
javax.faces.component.UIInput.CONVERSION=Input data is not in the correct type.
javax.faces.component.UIInput.REQUIRED=Required value is missing.
业务逻辑层和Spring Framework
业务对象和业务服务存在于业务逻辑层中Q一个业务对象不仅包含数据,而且包含相应的逻辑Q在范例应用中包含三个业务对象:(x)Product、Category和User?br />
业务服务与业务对象交互ƈ提供更高U的业务逻辑Q需要首先定义一个正式的业务接口Q它是直接与l端用户交互的服务接口。在JCatalog中,通过?Spring Framework帮助下的POJO实现业务逻辑层,其中共有两个业务服务QCatalogService包含Catalog理相关的业务逻辑Q?UserService中包含User理逻辑?br />
Spring是基于IoC概念的框Ӟ在范例应用中用到的SpringҎ(gu)包括:(x)
在范例应用中Q如果一个具有重复ID的新产品被插入,会(x)抛出DataIntegrityViolationExceptionQ这一异常被
catchqrethrown一个DuplicateProductIdException。这P该异常就可以与其它的异常区别处理?br />
Integration层和Hibernate
Hibernate是一个开源的ORM框架Q它可以支持所有主SQL数据库系l,Hibernate的查询语a为对象和关系架v了非常好的桥梁。Hibernate提供了强大的功能以实玎ͼ(x)数据d和更新、事务管理、数据连接池、查询和实体关系理{?br />
Data Access Ojbect(DAO)
JCatalog中采用了Dao模式Q该模式抽象和封装了所有对数据源的讉KQ该应用中包括两个DAO接口QCatalogDao和UserDaoQ它们相应的实现HibernateCatalogDaoImpl和HibernateUserDAoImpl包含了Hibernate特定的逻辑来实现数据的理和持久化?br />
实现
现在我们来看看如何将上面讨论的这些东西包装在一起以实现JCatalogQ你可以从这个地址下蝲源码Q?a >source code
数据库设?/font>
我们范例应用创徏了包?个表的数据库Q如?所C:(x)
c设?/font>
?昄了JCatalog的类?br />
“编E到接口”的思想贯穿了整个设计实CQ在表示层,q到四个backing beanQProductBean、ProductListBean、UserBean和MessageBean;业务逻辑层包含两个业务服?(CatalogService和UserService)和三个业务对?Product、Category和User);Integration层有两个Dao接口和它们相应的Hibernate实现QSpring的application context用来理l大多数的业务逻辑层和integration层的对象QServiceLocatorJSF和业务逻辑层整合在了一赗?br />
Wire everything up
׃幅所限,我们仅D例说明,范例中use case CreateProduct展示了如何装配和构徏应用Q在详细讲述l节前,我们利用sequence??)来说明所有层的end-tp-end整合?br />
表示?/b>Q?br />表示层实现包括创建JSP面、定义页D、创建和配置backing bean以及(qing)JSF与业务逻辑层整合?br />
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;
}
Backing bean that contains product information.
ServletContext context = FacesUtils.getServletContext();
业务逻辑?/b>
this.appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
this.catalogService = (CatalogService)this.lookupService(CATALOG_SERVICE_BEAN_NAME);
this.userService = (UserService)this.lookupService(USER_SERVICE_BEAN_NAME);
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;
}
Integration?/b>
<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
uses HibernateTemplate
to integrate between Hibernate and Spring. Here's the configuration for HibernateTemplate
:
<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配置文g来映业务对象和关系数据库,在JCatalog中,Product.hbm.xml表示了Product对象的映,Category.hbm.xml则用来表CCategory的映,Product.hbm.xml如下Q?br />
l论
<!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
is wired with HibernateTemplate
by Spring:
<bean id="catalogDao" class="catalog.model.dao.hibernate.CatalogDaoHibernateImpl">
<property name="hibernateTemplate"><ref bean="hibernateTemplate"/></property>
</bean>
本文主要讲述了如何将JSF与Spring、Hibernate整合在一h构徏实际的Web应用Q这三种技术的l合提供了一个强大的Web应用开发框架。在Web应用的高层设计中应该采用多层构架体系QJSF非常适合MVC设计模式以实现表C层QSpring可用在业务逻辑层中理业务对象Qƈ提供事物理和资源管理等QSpring与Hibernatel合的非常出ԌHibernate是强大的O/R映射框架Q它可以在integration层中提供最好的服务?br />
通过整个Web应用分割成多层,q借助于“编E到接口”,应用E序的每一层所采用的技术都是可替换的,例如Struts可以用来替换JSFQJDO可替换Hibernate。各层之间的整合不是不值得研究Q采用IoC和ServiceLocator设计模式可得整合非常容易。JSF提供了其它Web框架Ơ缺的功能,然而,qƈ不意味着你马上抛弃Struts而开始用JSFQ是否采用JSF取决于项目目前的状况和功能需求,以及(qing)开发团队的意见{?br />
Struts和JSF/Tapestry都属于表现层框架Q这两种分属不同性质的框Ӟ后者是一U事仉动型的组件模型,而Struts只是单纯的MVC模式框架Q老外L急吼D事g驱动型就比MVC模式框架好,何以见得Q我们下面进行详l分析比较一下到底是怎么回事Q?/p>
首先事g是指从客L(fng)面Q浏览器Q由用户操作触发的事ӞStruts使用Action来接受浏览器表单提交的事Ӟq里使用了Command模式Q每个承Action的子c都必须实现一个方法execute?/p>
在struts中,实际是一个表单Form对应一个Actionc?或DispatchAction)Q换一句话_(d)(x)在Struts中实际是一个表单只能对应一个事Ӟstrutsq种事g方式UCؓ(f)application eventQapplication event和component event相比是一U粗_度的事件?/p>
struts重要的表单对象ActionForm是一U对象,它代表了一U应用,q个对象中至包含几个字D,q些字段是Jsp面表单中的input字段Q因Z个表单对应一个事Ӟ所以,当我们需要将事g_度l化到表单中q些字段Ӟ也就是说Q一个字D对应一个事件时Q单U用Struts׃太可能,当然通过l合JavaScript也是可以转弯实现的?/p>
而这U情况用JSF可以方便实玎ͼ
<h:inputText id="userId" value="#{login.userId}"> <f:valueChangeListener type="logindemo.UserLoginChanged" /> </h:inputText> |
#{login.userId}表示从名为login的JavaBean的getUserId获得的结果,q个功能使用struts也可以实玎ͼname="login" property="userId"
关键是第二行Q这里表C如果userId的值改变ƈ且确定提交后Q将触发调用cUserLoginChanged的processValueChanged(...)Ҏ(gu)?/p>
JSF可以为组件提供两U事Ӟ(x)Value Changed?Action. 前者我们已l在上节见识q用处,后者就相当于struts中表单提交Action机制Q它的JSF写法如下Q?/p>
<h:commandButton id="login" commandName="login"> <f:actionListener type=”logindemo.LoginActionListener?/> </h:commandButton> |
从代码可以看出,q两U事件是通过Listernerq样观察者模式脓(chung)在具体组件字D上的,而Struts此类事g是原始的一U表单提交Submit触发机制。如果说前者比较语a化(~程语言?fn)惯做法cMSwing~程Q;后者是属于WEB化,因ؓ(f)它是来自Html表单Q如果你h是从Perl/PHP开始,反而容易接受Strutsq种风格?/p>
基本配置
Struts和JSF都是一U框ӞJSF必须需要两U包JSF核心包、JSTL包(标签库)Q此外,JSFq将使用到Apache目的一些commons包,q些Apache包只要部|在你的服务器中既可?/p>
JSF包下载地址Q?a target="_blank">http://java.sun.com/j2ee/javaserverfaces/download.html选择其中Reference Implementation?/b>
JSTL包下载在http://jakarta.apache.org/site/downloads/downloads_taglibs-standard.cgi
所以,从JSF的驱动包l成看,其开源基因也占据很大的比重,JSF是一个SUN伙伴们工业标准和开源之间的一个血ѝ?/p>
上述两个地址下蝲的jar合ƈ在一起就是JSF所需要的全部驱动包了。与Struts的驱动包一Pq些驱动包必M于Web目的WEB-INF/libQ和Struts一L(fng)是也必须在web.xml中有如下配置Q?/p>
<web-app> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> |
q里和Struts的web.xml配置何其怼Q简直一模一栗?/p>
正如Struts的struts-config.xml一PJSF也有cM的faces-config.xml配置文gQ?/p>
<faces-config> <navigation-rule> <from-view-id>/index.jsp</from-view-id> <navigation-case> <from-outcome>login</from-outcome> <to-view-id>/welcome.jsp</to-view-id> </navigation-case> </navigation-rule> <managed-bean>
|
在Struts-config.xml中有ActionForm Action以及(qing)Jsp之间的流E关p,在faces-config.xml中,也有q样的流E,我们具体解释一下NavigationQ?/p>
在index.jsp中有一个事Ӟ(x)
<h:commandButton label="Login" action="login" />
action的值必d配form-outcome|上述Navigation配置表示Q如果在index.jsp中有一个login事gQ那么事件触发后下一个页面将是welcome.jsp
JSF有一个独立的事g发生和页面导航的程安排Q这个思\比struts要非常清晰?/p>
managed-beancMStruts的ActionFormQ正如可以在struts-config.xml中定义ActionForm的scope一Pq里也定义了managed-bean的scope为session?/p>
但是如果你只以ؓ(f)JSF的managed-beanp点功能就错了QJSF融入了新的Ioc模式/依赖性注等技术?/p>
Ioc模式
对于Userbeanq样一个managed-beanQ其代码如下Q?/p>
public class UserBean {
private String name;
private String password;
// PROPERTY: name
public String getName() { return name; }
public void setName(String newValue) { name = newValue; }
// PROPERTY: password
public String getPassword() { return password; }
public void setPassword(String newValue) { password = newValue; }
}
<managed-bean> |
faces-config.xmlq段配置其实是将"me"赋值给nameQ将secret赋值给passwordQ这是采?a target="_blank">Ioc模式中的Setter注射方式?/p>
Backing Beans
对于一个web formQ我们可以用一个bean包含其涉?qing)的所有组Ӟq个beanq为Backing BeanQ?Backing Bean的优Ҏ(gu)Q一个单个类可以装相关一pd功能的数据和逻辑?/p>
说白了,是一个Javabean里包含其他JavabeanQ互相调用,属于Facade模式或Adapter模式?/p>
对于一个Backing Beans来说Q其中包含了几个managed-beanQmanaged-bean一定是有scope的,那么q其中的几个managed-beans如何配置它们的scope呢?
<managed-bean> ... <managed-property> <property-name>visit</property-name> <value>#{sessionScope.visit}</value> </managed-property> |
q里配置了一个Backing Beans中有一个setVisitҎ(gu)Q将q个visit赋gؓ(f)session中的visitQ这样以后在E序中我们只访问visit对象Q从中获取我们希望的数据Q如用户登陆注册信息Q,而visit是保存在sessionq是application或request只需要配|既可?/p>
UI界面
JSF和Struts一P除了JavaBeanscM外,q有面表现元素Q都是是使用标签完成的,Struts也提供了struts-faces.tld标签库向JSFq渡?/p>
使用Struts标签库编E复杂页面时Q一个最大问题是?x)大量用logic标签Q这个logic如同if语句Q一旦写hQ搞的JSP面象俄|斯方块一P但是使用JSF标签qz优:(x)
<jia:navigatorItem name="inbox" label="InBox" |
如果authenticationBean中inboxAuthorizedq回是假Q那么这一行标{ְ不用昄Q多q净利烦Q?/p>
先写到这里,我会(x)l箋对JSF深入比较下去Q如果研I过Jdon框架的hQ可能会(x)发现QJdon框架的jdonframework.xml中service配置和managed-bean一样都使用了依赖注,看来对Javabean的依赖注已l迅速地成ؓ(f)一U新技术象征,如果你还不了解Ioc模式Q赶紧补课?/p>
附Jsf核心教程一个JSF案例:login.rar
相关讨论Q?/p>