EE6愿望清单第二部分:JSF

发布者:    |       Java EE

这是该系列的第二部分。第一部分在这里

http://blog.hibernate.org/cgi-bin/blosxom.cgi/2007/03/30#ee6part1

我是JSF的粉丝,并不是因为JSF在各方面都完美无缺,而是我喜欢其整体架构,并认为其瑕疵和限制比我所使用的其他Web框架更容易修复。当然,无论我对其他框架有何感受,忽略JSF都是愚蠢的。JSF无疑是Java空间中增长最快的Web框架。它已经是最受欢迎的框架之一,仅次于Struts。凭借其开放标准,JSF培养了一个丰富的扩展和组件库生态系统。但如果你对JSF与其他方法相比是否提供任何有说服力的技术优势表示怀疑,以下是我认为模型优越的原因

  • 应用程序组件是普通的Java对象(JSF管理Bean),通过EL与用户界面和编排逻辑绑定。可以编写业务逻辑组件,不依赖JSF,并直接从JSF页面使用它们,无需用Java编写的中间表示层
  • 管理Bean是有状态的和上下文相关的。尽管与Seam相比,上下文模型极为有限,但这个问题是可修复的,正如Seam所展示的那样。
  • 一些人认为JSF的生命周期复杂,但它非常适合与Seam这样的应用程序框架一起使用,其中业务逻辑直接绑定到视图。特别是,验证的阶段性处理、随后是模型更新、然后是事件处理,最后是视图渲染,这正是如果模型对象可能是受管实体时所需要的。任何其他生命周期都不会起作用。
  • 用户界面通常使用标记语言定义。标记语言,由于其层次结构,自然映射到用户界面的结构。(我从未觉得在过程代码中定义我的层次结构用户界面很舒服,比如Swing、Wicket或GWT。)然而,有些独特的是,如果你希望使用过程代码来创建或操作你的用户界面,JSF也支持这一点。
  • 更好的是,这种标记语言不仅限于HTML提供的原始元素集。HTML,我想,是定义超链接文本文档的合理语言。但它从未被设计用来设计今天的丰富互联网应用,显然在完成这项任务方面是不够的。JSF提供了扩展HTML,使用新语言元素定义丰富用户界面的手段。有些人认为,使用JSF特定的标记元素打破了传统的设计师/开发者角色分离。但在HTML中,正确的角色分离是开发者创建语义HTML,设计师创建CSS。此外,这种论点预先假设设计师无法了解JSF标记元素的功能——在我看来,这严重低估了优秀网页设计师的智力。
  • JSF UI组件模型解决了影响纯HTML表单处理和许多其他Web框架的某些问题。同一个表单上的多个提交按钮可以轻松绑定到不同的服务器端操作。具有重复输入元素的表单(想想在网格中排列的输入)可以轻松映射回服务器端模型对象。

然而,JSF 1.2并没有处理好一些事情,这要求在Facelets、Seam、Shale、Ajax4JSF、ICEFaces、Avatar等产品中开发非标准扩展。有些人认为,如果你需要使用非标准化扩展,你最好完全避免标准。这种推理方法有三个问题

  • JSF标准的主要受众不是应用程序开发者。主要受众是丰富用户界面组件库的开发者。在这里,JSF取得了巨大的成功!在开源(Richfaces、ICEFaces、Trinidad、Woodstock、RCFaces等)以及许多商业产品中,JSF提供了组件开发者构建的标准平台。
  • 如果你的选择是在80%的代码中遵循标准,在剩下的20%中使用一些标准的开源扩展,还是使用一个完全非标准的框架,那么你使用非标准的方法不会更好。你将糟糕80%。
  • 开发标准扩展的人们正在努力将这些扩展带回到JSF 2.0中。

这就是这个愿望清单的原因。作为JSF EG的JBoss代表,这些是我将在JSF 2.0中推动的事情。

异步部分提交和渲染

这是一个显而易见的事实,大家已经一致认为这是必需的。当JSF 1.2最终确定时,Ajax热潮发生了。JSF实际上是一个优秀的丰富互联网应用组件模型,Ajax4JSF、Avatar和ICEFaces等项目清楚地证明了这一点。不幸的是,目前Ajax化的JSF组件库共存是一个噩梦。这个问题只能通过在规范中定义部分提交和渲染的工作方式来解决。

基于注解的编程模型

像Java网络层中的其他部分一样,JSF陷入了2003年的困境。管理Bean的定义需要EJB2/Spring级别的XML繁琐。有两种方法可以解决这个问题。一种选择是引入注解来定义管理Bean的名称、作用域和依赖注入。第二种选择是简单地依赖于Web Beans组定义的组件模型。作为JSR-299的召集人,我当然同情第二种选择,但我不确定JSF EG的其他成员将如何对这一建议做出反应。

转换器、验证器和UI组件也应该可以通过注解定义。

非faces请求的增强生命周期

JSF在处理JSF表单提交的生命周期上投入了大量的爱心和关注。它还简要地提到了一个叫做非JSF请求的东西。最有趣的非JSF请求类型是HTTP GET请求,当你这么想的时候——这实际上是最常见的请求类型。在这里,规范令人失望。当然,可以创建带有请求参数的可书签JSF页面,但你会失去一个抽象级别,并最终编写类似servlet的代码。

Seam通过提供页面操作页面参数来解决此问题,这些与为faces请求提供的抽象类似。(它们看起来也像基于动作的Web框架如Struts或WebWork提供的功能。)

JSF 2.0应该为非JSF请求定义一个生命周期,包括

  • 对请求参数的验证和模型更新
  • 处理验证失败的一些机制
  • 动作调用
  • 将请求重定向到不同URL的一些设施

换句话说,非JSF请求将需要提供faces请求目前提供的一切。唯一的不同之处在于,提交的值不是来自JSF输入组件,而是来自普通的HTTP参数(例如,在可书签的URL中)。

有趣的问题是,这些动作、请求参数到模型属性的映射以及验证应该在哪里声明。Seam今天提供的解决方案是在XML文档中声明这些,包括编排逻辑(导航)。但我越来越倾向于将这些嵌入到页面定义中。例如

<f:view>
   <f:parameter name="customerName" value="#{customerFinder.name}" required="true">
      <f:validateLength max="100"/>
   </f:parameter>
   <f:onRender action="#{customerFinder.findByName}"/>
   ...
</f:view>

改进的编排

JSF导航规则提供了如果你曾经使用过类似Struts或WebWork的东西所期望的基本功能。你可以编写一个返回字符串值结果的动作方法,并在XML中定义导航规则,以确定要渲染或重定向到的视图。以下是需要迫切解决的一些改进

  • 结果不需要是字符串——任何具有toString()方法的对象都应该可以工作
  • 如果动作方法不需要返回结果,那就更加透明和优雅——导航规则可以指定一个要评估的值表达式
  • 执行重定向的导航规则应该能够指定用于重定向的请求参数列表(其中参数值使用值表达式定义)
  • 导航规则应该能够指定HTTP错误代码作为结果
  • 应该可以编写异常处理规则,当特定异常类型从JSF生命周期阶段传播出来时被触发

以下类型的操作是可能的

<navigation-rule>
   <from-action>#{customerFinder.findByName}</from-action>
   <navigation-case>
      <if>#{customerFinder.result!=null}</if>
      <to-view-id>/displayCustomer.xhtml</to-view-id>
      <redirect>
         <parameter-name>customerId</parameter-name>
         <parameter-value>#{customerFinder.result.id}</parameter-value>
      </redirect>
   </navigation-case>
</navigation-rule>

(Seam今天通过定义自己的基于XML的导航规则语言提供所有这些功能。)

错误处理

JSF缺乏异常处理设施是一个严重的缺陷,完全忽视了Java中当前最佳的异常处理实践。不编写servlet过滤器就无法进行任何集中的异常处理。更糟糕的是,Unified EL具有完全病态的行为,它会将所有在调用管理Bean期间发生的异常(即使是运行时异常)都包装在完全无用的ELEvaluationException中。这两个问题都应该得到解决。

对于第一个问题的一个非常激动人心的解决方案是对JSF生命周期对象使用责任链模式进行重新设计。每个生命周期阶段都是链中的一个元素,并负责将任务委托给下一个阶段。应用程序可以在链中的任何位置添加新的阶段,这将提供通过环绕样式拦截集中处理异常的可能性。然后我们可以废弃那个有用但有限且不够优雅的PhaseListener扩展点。这种方法将使JSF更具可扩展性。

标准化Facelets并简化JSF UI组件的开发

JSF需要一个自己的模板语言。Facelets是一个完美的起点。应该废弃对JSP的支持。

幸运的是,现在有如此多的JSF组件库。因为自己开发控件是非常痛苦的事情。最大的问题是JSP。引入一个标准的JSF特定模板语言应该会极大地改善这种情况,并使JSF UI组件的开发对普通用户更加容易。模板语言还可以轻松定义由其他组件组成的组件,就像Facelets今天所做的那样。

替代无状态UI组件生命周期

JSF的UI组件树是有状态的,这意味着组件树在faces请求(表单提交)之间保持。这是一个很好的特性,可以让JSF处理条件渲染和重复输入的表单,以及程序化操作组件的表单。然而,在简单(且常见)的情况下,状态是过度设计的。大多数表单没有条件渲染的控件或控件网格。JSF需要一个替代的无状态生命周期来处理常见情况。

一个可能的选择是模仿Tapestry。Tapestry区分了“简单”表单(没有重复或条件控件)和“复杂”表单(有)。对于简单表单,Tapestry使用无状态生命周期。对于复杂表单,Tapestry将组件树的一些信息序列化到客户端,并在“回放”阶段重建组件树时使用这些信息。(Tapestry对复杂表单的处理在概念上与JSF中使用的有状态模型非常相似。)

数据绑定

数据绑定是一个目前未明确指定且导致许多新用户问题的难题。JSF的DataModel类是解决方案的一个草图,但它远远不足以满足实际需求。我对一个更好的解决方案有了一些概念,但在此成为具体建议之前还需要大量的工作。

基于模型的验证

JSR-303正在定义一个为Java平台定义基于模型验证的标准设施。这个新设施必须与JPA和JSF集成。在Seam中,Hibernate Validator提供了这个功能,Seam为JSF和Hibernate Validator之间提供的集成可以成为JSF 2.0的一个可能模式。

允许在消息中使用EL

JSF对EL的使用是整个架构的一个真正强点。然而,缺少了一个小细节:定义在资源包和FacesMessages中的消息应该支持嵌入式值表达式的插值。(Seam已经支持这一点。)

例如,你可以在你的JSF页面中有以下文本

#{messages['myapp.welcome']}

并在messages_en.properties中

myapp.welcome=Welcome, #{user.firstName} #{user.lastName}!

会话失效

令人难以置信的是,JSF没有提供用于使HTTPSession失效的API。

安全

JSF不提供任何专门的功能来进行身份验证或授权。我不确定我们在这里应该做什么(如果有),但至少我们需要讨论这个问题。


返回顶部