跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

这是您第一次登陆到 developerWorks,已经自动为您创建了您的概要文件。 选择您概要文件中可以公开的信息的信息(如姓名、国家/地区,以及公司),这些信息同时也会与您所发布的内容相关联。 您可以随时更新您的 IBM 账号。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

JSF 2 简介,第 1 部分: 流线化 Web 应用程序开发

使用 JSF 2 简化导航、免除 XML 配置并轻松访问资源

David Geary, 总裁, Clarity Training, Inc.
David Geary
David Geary 是一名作家、演讲家和顾问,也是 Clarity Training, Inc. 的总裁,他指导开发人员使用 JSF 和 Google Web Toolkit (GWT) 实现 Web 应用程序。他是 JSTL 1.0 和 JSF 1.0/2.0 专家组的成员,与人合作编写了 Sun 的 Web Developer 认证考试的内容,并且为多个开源项目作出贡献,包括 Apache Struts 和 Apache Shale。David 的 Graphic Java Swing 一直是有关 Java 的畅销书籍,而 Core JSF(与 Cay Horstman 合著)是有关 JSF 的畅销书。David 经常在各大会议和用户组发表演讲。他从 2003 年开始就一直是 NFJS tour 的固定演讲人,并且在 Java University 教授课程,两次当选为 JavaOne 之星。

简介: 随着 2.0 版本的发布,Java™Server Faces (JSF) 现在可以轻松地实现健壮的、Ajax 风格的 Web 应用程序。本文是共三部分的系列文章的开篇,JSF 2.0 专家组成员 David Geary 将展示如何利用 JSF 2 中的新特性。在这期文章中,您将了解到如何使用 JSF 2 流线化开发,您将使用注释和约定代替 XML 配置,简化导航,并轻松访问资源。并且您将看到如何在您的 JSF 应用程序中使用 Groovy。

查看本系列更多内容

发布日期: 2009 年 6 月 15 日
级别: 中级 其他语言版本: 英文
访问情况 : 12227 次浏览
评论: 


有关 Web 应用程序框架的最佳发源地,人们一直争论不休:是象牙塔(由理论家空想而来)还是现实世界。在后一种情况下,框架的诞生经受了实际需求的严酷考验。凭直觉判断,经受了实际需求的考验要胜过理论家的空想,并且我认为这种直觉完全经得起更进一步的检验。

JSF 1 就是在象牙塔中开发的,因此,它的出现并没有引起太大的轰动。但是,JSF 做对了一件事情 — 它使市场上出现了大量来自实际开发的创新。早些时候,Facelets 的初次登场成为了 JavaServer Pages (JSP) 的强有力候补。然后出现了 Rich Faces,一个出色的 JSF Ajax 库;接着是 ICEFaces,将 Ajax 和 JSF 联合起来的新颖方法;还有 Seam、Spring Faces、Woodstock 组件、JSF Templating,等等。所有这些开源 JSF 项目都是由开发人员根据自己需要的功能构建的。

JSF 2.0 专家组实际上对来自这些开源项目的最佳特性进行了标准化。尽管 JSF 2 规范确实是由一些理论家编写的,但它也受到了来自实际开发的创新的驱动。回想起来,专家组的工作其实非常轻松,因为我们正站在巨人的肩膀上,比如 Gavin King (Seam)、Alexandr Smirnov (Rich Faces)、Ted Goddard (ICEFaces) 和 Ken Paulson (JSF Templating)。实际上,所有这些巨人都是 JSF 2 专家组的成员。因此,JSF 2 在许多方面都结合了象牙塔和真实世界的长处。并且它展示了这一点。JSF 2 是对 JSF 1 的重大改进。

本文是共三部分的系列文章的开篇,主要有两个目标:展示激动人心的 JSF 2 新特性,展示如何最佳地利用这些特性,这样您就能够利用 JSF 2 提供的功能。通过演示 JSF 2 的应用并伴随一些最佳使用技巧,我将阐述前面两个问题。下面是本文将要介绍的技巧:

  • 技巧 1:去除 XML 配置
  • 技巧 2:简化导航
  • 技巧 3:使用 Groovy
  • 技巧 4:利用资源处理程序

但是,我将首先介绍贯穿整个系列文章的示例应用程序。本文的应用程序源代码可以 下载 获得。

基于强制映射的 Web 服务 mashup 示例

图 1 展示了一个 JSF mashup — 我将它称为 places 应用程序 — 它使用 Yahoo! Web 服务将地址转换为地图,并显示缩放级别和天气预报:


图 1. 从 Yahoo! Web Services 中查看地图和天气信息
从 Yahoo! Web Services 中查看地图和天气信息

要创建一个地点,需要填写地址表单,激活 Go 按钮,然后应用程序将把地址发送给两个 Web 服务:Yahoo! Maps 和 Yahoo! Weather。

Map 服务在 Yahoo! 服务器上返回指向地址映射的 11 个地图 URL,使用不同的缩放级别。Weather 服务返回一些预先组装的 HTML。图像 URL 和 HTML 内容都轻松地显示在一个 JSF 视图中,这要分别感谢 <h:graphicImage><h:outputText>

places 应用程序使您能够输入任意数量的地址。您甚至可以多次使用同一个地址 ,如图 2 所示,它实际上演示了缩放级别:


图 2. 缩放级别
缩放级别

应用程序的关键点

places 应用程序有 4 个托管 bean(managed bean),如 表 1 所示:


表 1. places 应用程序中的托管 bean
托管 bean 名称范围
mapService com.clarity.MapService 应用程序
weatherService com.clarity.WeatherService 应用程序
places com.clarity.Places 会话
place com.clarity.Place 请求

运行 places 应用程序

要运行 places 应用程序,需要访问 developer.yahoo.com/maps/ajax,从 Yahoo! 获得一个应用程序 ID,这样才能使用 Yahoo! Web 服务。单击 Yahoo! Maps Web Service 中的 Get an App ID 按钮。得到 ID 后,在 MapService.javaWeatherService.java 中用您的 ID 替换 YOUR_ID_HERE

应用程序在会话范围内存储了一组 Place,如 图 1 所示,并在请求范围内维护了一个 Place。应用程序还分别使用应用程序范围内的 mapServiceweatherService 托管 beans 为 Yahoo! 的 map 和 weather Web 服务提供了简单的 API。

创建地点非常简单。清单 1 显示了 图 1 中的视图所含的地址表单的代码:


清单 1. 地址表单
				
<h:form>
  <h:panelGrid columns="2">
    #{msgs.streetAddress} <h:inputText value="#{place.streetAddress}" size="15"/>
    #{msgs.city}          <h:inputText value="#{place.city}"          size="10"/>
    #{msgs.state}         <h:inputText value="#{place.state}"         size="2"/>
    #{msgs.zip}           <h:inputText value="#{place.zip}"           size="5"/>

    <h:commandButton value="#{msgs.goButtonText}"
        style="font-family:Palatino;font-style:italic"
        action="#{place.fetch}"/>

  </h:panelGrid>
</h:form>

当用户激活 Go 按钮并提交表单后,JSF 将调用按钮的操作方法:place.fetch()。该方法将信息从 Web 服务发送到 Place.addPlace(),后者创建一个新的 Place 实例,使用传递给方法的数据初始化实例,并将其存储在请求范围内。

清单 2 展示了 Place.fetch()


清单 2. Place.fetch() 方法
				
public class Place {
  ...
  private String[] mapUrls
  private String weather
  ...
  public String fetch() {
    FacesContext fc = FacesContext.getCurrentInstance()
    ELResolver elResolver = fc.getApplication().getELResolver()

    // Get maps

    MapService ms = elResolver.getValue(
      fc.getELContext(), null, "mapService")

    mapUrls = ms.getMap(streetAddress, city, state)

    // Get weather

    WeatherService ws = elResolver.getValue(
      fc.getELContext(), null, "weatherService")

    weather = ws.getWeatherForZip(zip, true)

    // Get places

    Places places = elResolver.getValue(
      fc.getELContext(), null, "places")

    // Add new place to places

    places.addPlace(streetAddress, city, state, mapUrls, weather)

    return null
  }
}

Place.fetch() 使用 JSF 的变量分解器(resolver)查找 mapServiceweatherService 托管 bean,并且使用这些托管 bean 从 Yahoo! Web 服务获得地图和天气信息。随后 fetch() 调用 places.addPlace(),后者使用地图和天气信息以及地址,在请求范围内创建一个新的 Place

注意 fetch() 返回 null。由于 fetch() 是一个与按钮有关的操作方法,null 返回值使得 JSF 重新加载同一个视图,其中显示用户会话中的所有位置,如清单 3 所示:


清单 3. 在视图中显示位置
				
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">

  <h:form>
    <!-- Iterate over the list of places -->
    <ui:repeat value="#{places.placesList}" var="place">

      <div class="placeHeading">
        <h:panelGrid columns="1">

          <!-- Address at the top -->
          <h:panelGroup>
            <div style="padding-left: 5px;">
              <i><h:outputText value="#{place.streetAddress}"/></i>, 
              <h:outputText value="#{place.city}"/>
              <h:outputText value="#{place.state}"/>
              <hr/>
            </div>
          </h:panelGroup>

          <!-- zoom level prompt and drop down -->
          <h:panelGrid columns="2">
            <!-- prompt -->
            <div style="padding-right: 10px;margin-bottom: 10px;font-size:14px">
              #{msgs.zoomPrompt}
            </div>

            <!-- dropdown -->
            <h:selectOneMenu onchange="submit()"
                 value="#{place.zoomIndex}"
                 valueChangeListener="#{place.zoomChanged}"
                 style="font-size:13px;font-family:Palatino">

              <f:selectItems value="#{places.zoomLevelItems}"/>

            </h:selectOneMenu>
          </h:panelGrid>

          <!-- The map -->
          <h:graphicImage url="#{place.mapUrl}" style="border: thin solid gray"/>

        </h:panelGrid>

        <!-- The weather -->
        <div class="placeMap">
          <div style="margin-top: 10px;width:250px;">
            <h:outputText style="font-size: 12px;"
              value="#{place.weather}"
              escape="false"/>
          </div>
        </div>
      </div>

    </ui:repeat>
  </h:form>

</ui:composition>

清单 3 中的代码使用 Facelets <ui:repeat> 标记迭代用户会话中存储的位置列表。对于每个位置,输出应当如图 3 所示:


图 3. 视图中显示的位置
视图中显示的位置

修改缩放级别

zoom 菜单(参见 图 3清单 3)有一个 onchange="submit()" 属性,因此当用户选择某个缩放级别时,JavaScript submit() 函数提交菜单的环绕(surrounding)表单。提交表单后,JSF 调用菜单的相关值修改侦听器 — Place.zoomChanged() 方法,如清单 4 所示:


清单 4. Place.zoomChanged()
				
public void zoomChanged(ValueChangeEvent e) {
  String value = e.getComponent().getValue()
  zoomIndex = (new Integer(value)).intValue()
}

Place.zoomChanged() 在一个名为 zoomIndexPlace 类的成员变量中存储缩放级别。由于导航不会受到与服务器通信的影响,JSF 将重新加载页面,并且地图使用新的缩放级别进行更新,如下所示:<h:graphicImage url="#{place.mapUrl}..."/>。当绘制地图时,JSF 调用 Place.getMapUrl(),它返回当前缩放级别下的地图 URL,如清单 5 所示:


清单 5. Place.getMapUrl()
				
public String getMapUrl() {
  return mapUrls == null ? "" : mapUrls[zoomIndex]
}

使用少量 Facelets

如果曾经使用过 JSF 1,那么很可能会注意到本文的 JSF 2 代码中存在一些细微的差别。首先,我使用了 JSF 2 的新的显示技术 — Facelets — 而不是 JSP。您将从本系列后续文章中看到,Facelets 提供了许多强大的特性来帮助您实现健壮、灵活和可扩展的用户界面。但是在前面的代码清单中,我并没有过多利用这种功能。然而,Facelets 为 JSF 带来的众多微小改进之一便是能够将 JSF 值表达式直接放入到 XHTML 页面;例如,在 清单 1 中,我将 #{msgs.city} 等表达式直接放入页面中。如果使用 JSF 1,则必须将表达式封装到 <h:outputText> 中,例如 <h:outputText value="#{msgs.city}"/>。但要注意,出于安全考虑,必须始终将来自用户输入的文本进行转义,例如,在 清单 3 中我使用了 <h:outputText>,它在默认情况下转义其文本来显示位置信息。

从 Facelets 角度来讲,还需要注意 清单 3 中的 <ui:composition> 标记。该标记指定清单 3 中的 XHTML 页面将被包含到其他 XHTML 页面中。Facelets composition 是 Facelets templating 的中心组件,类似于流行的 Apache Tiles 框架。在本文的后续文章中,我将讨论 Facelets 模板并展示如何根据 Composed Method Smalltalk 模式构建您的视图。

目前为止,前面的代码并没有使用 Facelets,与 JSF 1 相比没有出现显著的变化。现在,我将展示更加大的差异。第一个比较大的差异体现在将要为 JSF 2 应用程序编写的 XML 配置的数量方面。


技巧 1:去掉 XML 配置

Web 应用程序的 XML 配置始终是个麻烦问题 — 它非常冗长并且容易出现错误,因此最好将 XML 配置委托给一个框架,比如通过注释、约定或特定于领域的语言。作为开发人员,我们应该能够集中精力实现一些操作,而不是将浪费时间在冗长的 XML 方面。

作为一个典型的例子,清单 6 展示了在使用 JSF 1 的情况下,在 places 应用程序中声明托管 bean 所需的 20 行 XML 代码:


清单 6. JSF 1 的托管 bean 声明
				
<managed-bean>
  <managed-bean-class>com.clarity.MapService</managed-bean-class>
  <managed-bean-name>mapService</managed-bean-name>
  <managed-bean-scope>application</managed-bean-scope>
</managed-bean>

<managed-bean>
  <managed-bean-class>com.clarity.WeatherService</managed-bean-class>
  <managed-bean-name>weatherService</managed-bean-name>
  <managed-bean-scope>application</managed-bean-scope>
</managed-bean>

<managed-bean>
  <managed-bean-class>com.clarity.Places</managed-bean-class>
  <managed-bean-name>places</managed-bean-name>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

<managed-bean>
  <managed-bean-class>com.clarity.Place</managed-bean-class>
  <managed-bean-name>place</managed-bean-name>
  <managed-bean-scope>request</managed-bean-scope>
</managed-bean>

对于 JSF 2,XML 消失了,您将对类使用注释,如清单 7 所示:


清单 7. JSF 2 的托管 bean 注释
				
@ManagedBean(eager=true)
public class MapService {
  ...
}

@ManagedBean(eager=true)
public class WeatherService {
  ...
}

@ManagedBean()
@SessionScoped
public class Places {
  ...
}

@ManagedBean()
@RequestScoped
public class Place {
  ...
}

按照约定,托管 bean 的名称与类名相同,类名的第一个字母被转换为小写。例如,清单 7 中创建的托管,从上到小依次为:mapServiceweatherServiceplacesplace。也可以使用 ManagedBean 注释的 name 属性显式地指定一个托管 bean,比如:@ManagedBean(name = "place")

清单 7 中,我对 mapServicewebService 托管 bean 使用 eager 属性。当 eager 属性为 true 时,JSF 将在启动时创建托管 bean 并将其放入应用程序范围。

也可以使用 @ManagedProperty 注释设置托管 bean 属性。表 2 展示了 JSF 2 托管 bean 注释的完整列表:


表 2. JSF 2 托管 bean 注释(@...Scoped 注释只对 @ManagedBean 有效)
托管 bean 注释描述属性
@ManagedBean

以托管 bean 的形式注册一个类实例,然后将其放入到使用其中一个 @...Scoped 注释指定的范围内。如果没有指定任何范围,JSF 将把此 bean 放入请求范围,如果没有指定任何名称,JSF 将把类名的第一个字母转换为小写,形成一个托管 bean 名称;例如,如果类名为 UserBean,那么 JSF 将创建一个托管 bean,其名为 userBeaneager name 属性都是可选的。

注释必须结合使用一个实现零参数构造器的 Java 类。

eagername
@ManagedProperty

为托管 bean 设置一个属性。注释必须放在类成员变量的声明之前。name 属性指定特性的名称,默认情况下为成员变量的名称。value 属性是特性的值,可以是一个字符串,也可以是一个 JSF 表达式,比如 #{...}

valuename
@ApplicationScoped 在应用程序范围内存储托管 bean。
@SessionScoped 在会话范围内存储托管 bean。
@RequestScoped 在请求范围内存储托管 bean。
@ViewScoped 在视图范围内存储托管 bean。
@NoneScoped 将托管 bean 指定为没有范围。无范围的托管 bean 在被其他 bean 引用时比较有用。
@CustomScoped

在定制范围内存储托管 bean。

定制范围就是指可以由页面创建者访问的地图。可以通过编程的方式控制定制范围内的 bean 的可视性和生命周期。value 属性指向一个地图。

value

从 faces-config.xml 中移除托管 bean 声明将极大地减少 XML,但是在 JSF 2 中,通过使用注释(如我对托管 bean 所做的一样)或是约定(比如 JSF 2 的简化的导航处理),几乎可以去掉所有的 XML 内容。


技巧 2:简化导航

在 JSF 1 中,导航使用 XML 指定。比如,要从 login.xhtml 转到 places.xhtml,可能使用清单 8 所示的导航规则:


清单 8. JSF 1 中的导航配置规则和用例
				
<navigation-rule>
  <navigation-case>
    <from-view-id>/pages/login.xhtml</from-view-id>
    <outcome>places</outcome>
    <to-view-id>/pages/places.xhtml</to-view-id>
  </navigation-case>
</navigation-rule>

要去除 清单 8 中的 XML,可以利用 JSF 2 的导航约定:JSF 将 .xhtml 添加到按钮操作的末尾并加载该文件。这意味着不需要使用注释或其他内容,只需要使用约定就可以完整地避免编写导航规则的需求。在清单 9 在,按钮的操作是 places,因此 JSF 加载 places.xhtml:


清单 9. 通过约定进行导航
				
<h:commandButton id="loginButton"
  value="#{msgs.loginButtonText}"
  action="places"/>

对于 清单 9 来说,不需要任何导航 XML。清单 9 中的按钮加载 places.xhtml,但是前提是该文件和按钮所在的文件处于同一个目录中。如果操作并没有以斜杠(/)开头,那么 JSF 认为这是一个相对路径。如果需要更加明确一点,可以指定一个绝对路径,如清单 10 所示:


清单 10. 使用绝对路径的导航
				
<h:commandButton id="loginButton"
  value="#{msgs.loginButtonText}"
  action="/pages/places"/>

当用户激活 清单 10 中的按钮时,JSF 将加载 /pages/places.xhtml 文件。

默认情况下,JSF 将从一个 XHTML 页面转至另一个 XHTML 页面,但是通过指定 faces-redirect 参数可以重定向,如清单 11 所示:


清单 11. 通过重定向进行导航
				
<h:commandButton id="loginButton"
  value="#{msgs.loginButtonText}"
  action="places?faces-redirect=true"/>


技巧 3:使用 Groovy

Java 技术的最大优势并不是 Java 语言,而是 Java 虚拟机(JVM)。在 JVM 上运行着强大、新颖和创新的语言,比如 Scala、JRuby 和 Groovy,这使您在编写代码时拥有了更多选择。Groovy 这个名字有些奇怪,但是功能非常强大,融合了 Ruby、Smalltalk 和 Java 语言,它是这些语言中最为流行的一种语言(参见 参考资料)。

使用 Groovy 的理由有很多。首先,它要比 Java 语言更加简洁、功能更加强大。还有两个原因:不使用分号,不需要强制转换(casting)。

您可能还没有注意到,在 清单 2 中,Place 类是使用 Groovy 编写的。这一点可以通过代码中没有使用分号看出来,但是注意下面这行代码:MapService ms = elResolver.getValue(...)。对于 Java 代码,我必须强制转换 ElResolver.getValue() 的结果,因为该方法返回类型 Object。Groovy 可以为我自动完成转换。

可以将 Groovy 用于任何使用 Java 代码编写的 JSF 工件 — 例如,组件、呈现器、验证器和转换器。事实上,这对于 JSF 2 来说并不新鲜 — 因为 Groovy 源文件编译为 Java 字节码,您只需使用 Groovy 生成的 .class 文件,就好象它们是由 javac 生成的一样。当然,Groovy 生成的 .class 文件可以正常工作后,需要了解如何热部署 Groovy 源代码,并且对于 Eclipse 用户,答案非常简单:下载并安装 Groovy Eclipse 插件(参见 参考资料)。Mojarra 是 Sun 的 JSF 实现,从版本 1.2_09 之后提供了对 Groovy 的明确支持(参见 参考资料)。


技巧 4:利用资源处理程序

JSF 2 提供了定义和访问资源的标准机制。您将自己的资源放到名为 resources 的顶级目录下,并使用一些 JSF 2 标记来在视图中访问这些资源。例如,图 4 展示了 places 应用程序的资源:


图 4. places 应用程序的资源
places 应用程序的资源

对资源的惟一需求是它必须位于 resources 目录或 resources 目录的子目录中。可以随意命名 resources 目录的子目录。

在您的视图代码中,可以使用两个 JSF 2 标记访问资源:<h:outputScript><h:outputStylesheet>。这些标记可以结合用于 JSF 2 的 <h:head><h:body> 标记,如清单 12 所示:


清单 12. 在 XHTML 中访问资源
				
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html">

  <h:head>
    ...
  </h:head>

  <h:body>
    <h:outputStylesheet library="css" name="styles.css" target="body"/>
    <h:outputScript library="javascript" name="util.js" target="head"/>
    ...
  </h:body>
</html>

<h:outputScript><h:outputStylesheet> 标记有两个属性,分别指定了脚本或样式表:librarynamelibrary 名称对应于 resources 目录下的子目录,这是保存资源的位置。例如,如果在 resources/css/en 目录中有一个样式表,那么 library 将为 css/enname 属性是资源本身的名称。

可重新定位的资源

开发人员需要能够在页面中指定想要呈现他们的资源的位置。例如,如果将 JavaScript 放在页面体中,浏览器将在加载页面时执行 JavaScript。另一方面,如果将 JavaScript 放到页面的头部,那么 JavaScript 只有在得到调用时才会被执行。由于资源的放置位置会影响它的使用方式,因此需要能够指定希望在哪些位置显示资源。

JSF 2 资源是可重新定位的,这意味着您可以在页面中指定希望放置资源的位置。您将使用 target 属性指定位置;比如,在 清单 12 中,我将 CSS 放到页面体中,而将 JavaScript 放到页面头部中。

有些情况下,需要使用 JSF 表达式语言(EL)访问资源。比如,清单 13 展示了如何使用 <h:graphicImage> 访问一个图像:


清单 13. 使用 JSF 表达式语言访问资源
				
<h:graphicImage value="#{resource['images:cloudy.gif']}"/>

清单 13 的非 EL 备选方法

无可否认,清单 13 中的语法不是很直观。它访问了一个 JSF 为了存储资源而创建的地图,因此很少需要使用这种语法。实际上,可以使用 <h:graphicImage/> 访问图像,而不需要使用 EL,比如:<h:graphicImage library="images" name="cloudy.gif"/>

在 EL 表达式内访问资源的语法是 resource['LIBRARY:NAME'],其中 LIBRARY NAME 对应于 <h:outputScript><h:outputStylesheet> 标记的 libraryname 属性。


结束语

到目前为止,我仅仅触及了 JSF 2 特性中最浅显的内容,包括托管 bean、注释、简化导航和资源支持。在本系列随后的两篇文章中,我将探讨 Facelets、JSF 2 的复合组件以及对 Ajax 的内置支持。



下载

描述名字大小下载方法
源代码jsf2fu1.zip1.9MBHTTP

关于下载方法的信息


参考资料

学习

获得产品和技术

讨论

关于作者

David Geary

David Geary 是一名作家、演讲家和顾问,也是 Clarity Training, Inc. 的总裁,他指导开发人员使用 JSF 和 Google Web Toolkit (GWT) 实现 Web 应用程序。他是 JSTL 1.0 和 JSF 1.0/2.0 专家组的成员,与人合作编写了 Sun 的 Web Developer 认证考试的内容,并且为多个开源项目作出贡献,包括 Apache Struts 和 Apache Shale。David 的 Graphic Java Swing 一直是有关 Java 的畅销书籍,而 Core JSF(与 Cay Horstman 合著)是有关 JSF 的畅销书。David 经常在各大会议和用户组发表演讲。他从 2003 年开始就一直是 NFJS tour 的固定演讲人,并且在 Java University 教授课程,两次当选为 JavaOne 之星。

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, Web development
ArticleID=391694
ArticleTitle=JSF 2 简介,第 1 部分: 流线化 Web 应用程序开发
publish-date=06152009
author1-email=david.mark.geary_@gmail.com
author1-email-cc=jaloi@us.ibm.com