目前应用很广泛而且也很成熟的Struts应用开发框架,在未来几年里将会逐渐淡出,其基于Action(action-based)的开发模型也将被活跃的java社区所推崇的新的开发模型所替代,对于java开源社区来说,追求***是很多人的理想。在2000年初诞生的Struts以及其他类似的 MVC(Model View Controler)框架主要都是以操作为中心,且都是无状态的开发模型,而现在,基于组件(component-based)和事件驱动(event- driven)的开发模型逐渐流行起来,在很多时候都成了Struts的有利竞争者,这其中来自jcp的jsr127-JavaServer Faces(JSF)以及来自apache的Tapestry是其中的佼佼者。
在这篇文章中,我们将把这两种框架JSF和Tapestry进行详细的对比。我们的比较将涉及到两种框架的设计、运行环境以及如何开发。比较的目的在于让读者了解这两种框架各自的优缺点,以便于在自己的项目中,根据实际情况,选择合适的框架。该文章的内容基于JSF1.1和Tapestry3.0.3 (在个别地方由于需要会谈到Tapestry4.0)。
JSF和Tapestry简介
JSF源于JCP(Java Community Process)的JSR127规范。Sun公司自己同时也对该规范提供了一个实现,目前的版本是1.1,而且该项目的设计者之一正是Struts的作者 Craig McClanahan。另外一个JSF的实现,就是Apache的一个项目MyFaces。目前,几个主流的java开发工具厂商都在其java ide中提供了对jsf的支持,比如Sun、Oracle、IBM、Borland等,而且JSF的后续版本JSF1.2也将成为J2EE5.0的一个组成部分。
Tapestry最初于2000年在SourceForge注册,来自于Apple WebObjects,随后,于2003年成为Apache的一个项目。与JSF不同的是,Tapestry并不是某一个jsr规范的实现,它仅仅是一个开源项目,当前版本是3.0.3。4.0版本预计也将在后面的一段时间内推出。
JSF和Tapestry作为MVC框架,在一些基本特性上是非常类似的:
◆它们都让开发者不再直接与Servlet API打交道,而是让开发者在一个更高的抽象层上思考问题;
◆它们都将web页面上的显示元素绑定到一个java对象的某个属性上,这些属性可能是字符串、数字、日期或者其他类型,并且由该对象来维护其状态。用户在页面上的交互行为(比如用鼠标点击一个按钮或者链接)都直接映射为java类中的一个事件处理方法;
◆两种框架都支持组件式的开发方式,并且开发的组件可被其他开发者重用。两种框架都自带一个标准组件库,提供web开发的常见通用功能。
在下面的章节里面,我们将会看见这两个框架在各自的实现方式上是有很大区别的。对于大多数程序员来说,基于两种框架做开发,将是非常不同的两种体验。
Sidebar: 例子程序在下面本文提供了一个例子程序,该程序的代码大部分都将在这片文章中做出详细的描述。这个程序主要就是一个管理个人假期的工具,它的主要功能包括:
◆一个home界面, 列出所有登记的假期,包括假期开始时间、天数以及一下描述信息;
◆一个detail界面,用于浏览某一登记假期的详细信息;
◆一个new界面,用于添加一个假期信息。
下面这幅图展示了该程序的主体流程和功能:
对比1: 页面开发(Page Development)
一个web应用实际上就是后端用java代码获取相应数据,并将数据传递给前端表现层代码,然后最终返回给终端用户。因此,在一个开发人员看来,当他初次接触JSF和Tapestry时,最直接的感觉就是JSF的表现层是基于JSP的模板技术,而Ta pestry的表现层模板基本就可以看作是一个HTML。
JSF
JSF采用JSP的技术作为其表现层技术。与标准兼容的JSF实现必须实现一个核心组件的JSP标签集。含有JSF标签的html页面不能在标准浏览器中预览。要想浏览,必须使用JSF设计工具或者直接部署到应用中,在真实运行环境中浏览。
对于JSF技术来说,其表现层技术就是JSP,但是这不是唯一的解决方法。Hans Bergsten的文章介绍了可供选择的其他的方法,读者可阅读该文章获取详细信息,Improving JSF by Dumping JSP 。 Bergsten的文章中指出了混合JSF和JSP标签暴露出的一些问题。当然,这些问题在JSF1.2和JSP2.1中会得到解决。
Tapestry
对于大多数的Tapestry应用来说,Tapestry的表现层模板看起来就是一个简单的规则的Html,只不过其中加入了一些Tapestry的属性。
大家可以尝试把代码放入一个Html的body标签当中,你会发现浏览器可以正常的观看它。
上面的例子中,jwcid = "@componentName"属性就是定义了一个Tapestry标签。
Tapestry的模板不仅仅可以用HTML作为载体,它也支持其他的一些标记语言,Tapestry标签是具有良好格式的标签,即必须成对出现。 Tapestry模板技术支持的标记语言典型的就是HTML以及用于无线应用的WML。其***的一个特点 就是,可脱离Servlet容器,直接预览。
事实上JSF标签由于不是标准的html标签,使得它对于初学者来说,是难于使用的。而对于很多java程序员来说,他们喜欢编辑HTML代码,至少是乐意编写html代码。
JSF技术宣称的一个技术优点就是,可使用同一个模板编写运行在不同设备上的应用,由此带来很大的灵活性。然而,这样做,由于要协调不同设备间的表现差异性,那么很可能同一个模板,将不能正好表现你的输出。同时,你不得不学习新的标签库的使用方法,并且搞懂它们是怎样映射到html的。随着时间的推移, JSF标签的简洁可帮助你很快的编写表现层代码,同时也降低了开发者对jsf设计工具的依赖。
本文认为,JSF的学习成本高于其技术优点。在大多数情况下,我们并不需要编写适应于不同设备的应用。尽管JSF设计工具提供了简单的图形化的方法来构建和预览JSF应用,但是在一个开发中,页面设计人员更多的是喜欢用流行的HTML设计工具来编写和预览页面,这就发生了一个冲突,即只有将更多的页面工作转移到java程序员身上,因为一个页面设计人员通常情况下是不乐意去操作JSF设计工具的。
JSF开发者一直在寻找一种解决这些问题的方法。JSF技术设计良好的扩展体系,使得这成为可能,其中一个技术浮现出来,那就是表现层控制器。一个非常有前途的表现层控制器的实现就是Facelets ,由java.net创建的开放源代码项目。Facelets的灵感就来源于Tapestry的模板模型,这使得JSF不再依赖于JSP技术。Facelets允许开发者创建Tapestry风格的标签。
在未来的一段时间内,Facelets将会被开发者所采用,或许会影响JSF未来的版本。和JSF比较,Tapestry在表现层方面基于HTML代码,可被标准浏览器所浏览,这正是它在表现层上的优势所在。
比较2: java编程模型(Java Programming Model)
在前面我们提到Tapestry和JSF都允许表现层的模板直接和一个Java类中的属性和方法进行交互,那这些类的实例在运行时是怎么创建和管理的呢?
Tapestry
Tapestry的一个完整过程通常都要包括三个部分:用于显示的页面模板(一般就是html),带有相关属性和方法的java类,用于定义页面模板上的控制元素和java类的关系的页面定义文件。Tapestry有着一套特殊的访问HttpSess ion、ServletContext的体系。
在一个页面定义文件中描述所有的数据绑定是可以的,但是一个页面控制元素却不能在request周期内,绑定一个java类。在request周期内只能通过一个page类访问一些属性和方法。这其中最主要的限制就是关于page类的问题,一个pag e类必须是BasePage或者AbstractPage的派生类。从另一方面来说,这就意味着你所编写的表现层逻辑的代码将会和框架本身的实现有着非常紧密的耦合。不过,Tapestry未来的版本就会减少这种耦合,努力成为一个松耦合的编程模型。
JSF
在JSF应用中没有页面定义文件。它只有一个全局的配置文件,命名为:faces-config.xml,里面通常都定义了一堆"managed beans"。这些managed beans都是带有属性和事件监听器的定义良好的java bean。在faces-config.xml中定义的后端bean都有三个参数:一个标识符、一个java类名、一个bean的生存周期,生存周期可以是request、session、application中的一种。一旦在 faces-config.xml中定义好了一个managed bean,那一个前端页面上的显示控制元素就可以使用标识符来关联这个bean。managed bean也可以配置为引用另外一个managed bean。
JSF和Tapestry都可以方便的与其他的中间层技术整合,比如Spring。JSF managed bean facility 是一个IoC(Inversion of Control)。通过诸如 JSF-Spring 这样的扩展技术,我们可以方便将其和Spring很好的整合在一起,使得JSF的表达式可以调用Spring的bean的方法。虽然Spring可以与 Tapestry3.0整合,但是在Tapestry4.0当中才能更好的充分发挥IoC特性;Tape stry的领导Howard Lewis Ship已经在Tapestry启动了IoC框架的工作。Spring的bean将会很轻松的注入到Tapestry应用的类中。
JSF的编程模型提供了更大的灵活型,因为你可以通过组合的方式来丰富你的代码功能。比如,你可以设定一个指定的managed bean完成页面的某些功能,同时,你也可以在这些bean里引用其他的managed bena,从而可在页面间共享一些功能。
而Tapestry必须使用类继承的方法,也就是说,Tapestry应用中的一个页面类必须从框架指定的基类中派生(包含一大堆框架指定的状态),这并不是一个理想的方法。
JSF能够非常直观的管理session和application周期内的状态: 页面代码可以方便的访问managed bean,而不管其生命周期是request、session还是application。而Tapestry在这方面就相对差点,不过, Tapestry4.0在这方面做了很大的改进,引入了类似于JSF managed bean的技术,同时,还支持Java 5.0的annotaions技术,减少对XML配置文件的依赖,从而降低应用配置的复杂性。
JSF在编程模型上来看,与Tapestry相比具有更大的灵活性。
对比3:请求处理生命周期(Request Processing Life Cycle)
请求处理生命周期在一个web应用中是很重要的,它反映了一个请求从提交到将信息返回给客户端的处理过程。当然,请求处理生命周期必须能以某种优雅的方式,在正确的地方插入用户定制的请求处理操纵逻辑。
JSF
JSF的请求处理生命周期清晰的定义成六个步骤:Restore View、Apply Request Values、 Process Validations、 Update Model Values、 Invoke Application 和 Render Response。从第二步Apply Request Values开始,可以直接跳到***一步Render Response,甚至可以直接返回给客户端并且通知JSF运行时,响应操作已经完成了。有些方法要访问JSF的FacesContext对象,比如状态监听器(phase listeners)、事件操纵器(event handlers)、转换器(converters)、验证器(validators)等,这都可能忽略请求处理生命周期中的某些步骤。
Tapestry
JSF有一个单一的生命周期模型,而Tapestry的生命周期依赖于其调用的引擎服务(engine service)。每一个引擎服务(engine service)都有其自己的生命周期。比如,Tapestry中的Direct Service控制标单的提交,而Page Service用于渲染页面,并且不需要额外的服务端操作。每一个引擎服务(engine service)都被设计成在自身的生命周期内完成一些特定的任务。这就意味着,对于一个特定的需求,你可以创建一个对应的引擎服务(engine service),并且可自己定制生命周期。
JSF的生命周期概念更容易理解,而Tapestry可对一个特定的操作定义一个生命周期,这在某些问题的解决上,可能会提供更优雅的解决方案。
【编辑推荐】