1.JSF的工作方式
JSF应用是通过处理由页面中组件触发的事件来工作的。这些事件是由用户的动作引起的。比如,当用户单击一个按钮时,按钮会触发一个事件,通过编写监听这个事件的监听器,JSF开发人员可以决定当特定事件发生时JSF应用应该做什么。也就是说,JSF应用是事件驱动的。图1说明了JSF应用的处理过程。
当一个事件发生时(比如,用户单击了一个按钮),事件通知通过HTTP发往服务器。服务器端使用叫作FacesServlet的特殊servlet处理该通知。Web容器里的每个JSF应用都有它自己的FacesServlet。
在后台,每个JSF请求都触发3件事情,如图2 JSF的工作方式。
为了处理JSF请求,它们必须交由FacesServlet处理,指定这种重定向是通过部署描述符中的servlet和servlet-mapping标记来实现的。
- <!-- Faces Servlet -->
- <servlet>
- <servlet-name>Faces Servlet</servlet-name>
- <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <!-- Faces Servlet Mapping -->
- <servlet-mapping>
- <servlet-name>Faces Servlet</servlet-name>
- <url-pattern>/faces/*</url-pattern>
- </servlet-mapping>
这表示所有请求的URL中都必须包含/faces/这个字符串模式,servlet-mapping元素下的url-pattern元素指定了这一点。注意:
可以指定一个上下文参数saveStateInClient并赋给它一个true值,以此来强制JSF把状态存放在客户端而不是服务器端。如果您打算这么做,必须在部署描述符中的servlet元素之前加入如下的context-param元素。
- <context-param>
- <param-name>saveStateInClient</param-name>
- <param-value>false</param-value>
- </context-param>
FacesServlet生成一个叫作FacesContext的对象,它包含了处理请求所必须的信息。更确切地说,FacesContext对象中包含Web容器传给FacesServlet的services方法的 ServletContext、ServletRequest及ServletResponse对象。在处理过程中,主要修改的就是这个 FacesContext对象。接着就是处理,处理器是一个叫作Lifecycle的对象。FacesServlet把控制权转交给Lifecycle,Lifecycle对象分6个阶段来处理FacesContext对象,我们稍后将会看到这些阶段。注意:
Lifecycle对象处理JSP请求所需的一系列动作称为请求处理生命周期(request processing lifecycle),这个词自始至终贯穿本书。
JSF的工作方式允许使用一个应用配置文件来配置JSF应用。讨论过Lifecycle对象的处理阶段后,我们会讨论如何使用这个配置文件来注册JavaBeans。
2.理解请求处理生命周期的各个阶段
Lifecycle对象分6个阶段来处理JSF请求(被封装在FacesContext对象里,而FacesContext对象就是处理过程中由Lifecycle读取并修改的对象),过程如下:
◆重建组件树 JSF应用里的JSP页面被表示成一个组件树。在这个阶段,通过重建这棵树来开始Lifecycle的处理过程。每个组件树都有一个在整个应用范围里惟一的标识符,此标识符是所请求URI的路径信息部分。比如,对于一个URI为/faces/index.jsp的请求,组件树的标识符就是 /index.jsp。生成的组件树保存在FacesContext对象中,以备后面的处理过程所用。
◆应用请求值 在这个阶段,使用请求里的当前值来更新每个组件的本地值。这些值可能来自请求参数、请求的报头及cookie等。在这个阶段的处理中,组件可以往事件队列里加入某些事件,这些事件可在随后的处理阶段处理。
◆处理验证 当每个组件的本地值被更新后,在此阶段中,Lifecycle对象会验证这些值的合法性。要求验证的组件必须提供验证逻辑的实现。作为选择,开发人员可以为一个组件注册零个或多个验证器。如果发现外部验证器,那么还会应用这些外部验证器里的验证逻辑来验证本地值。
◆更新模型值 只有当组件树中所有组件的本地值都通过验证后,才有可能到达该阶段。在这个阶段里,LifeCycle更新应用的模型数据。组件在这个阶段也可以排列事件。
◆调用应用 在这个阶段,JSF实现处理所有应用层次的事件,比如提交表单或链接到其他页面等。
◆呈现响应 在这个阶段,JSF实现将响应发回客户端。
由于请求处理生命周期里的应用请求值、处理验证、更新模型值和调用应用等阶段都可以在当前请求对应的FacesConetxt的实例里排列事件,因此,JSF实现必须在这些阶段后处理这些事件。
在两个阶段中间,Lifecycle对象会检查所有需要调用的事件监听器。当编写事件监听器时,需要选择事件监听器应在哪个阶段过后调用。或者,也可以编写一个在不同阶段后调用的事件监听器。图3表明了处理JSF请求的各个阶段。标有“处理事件”的方框表示Lifecycle对象在该处执行事件监听器。
图3 请求处理生命周期的各个阶段
注意,事件监听器可能会改变处理过程。它可以指示Lifecycle对象直接跳到***一个阶段,或是在当前事件处理完后马上退出。
3.使用应用配置文件注册JavaBeans
可以通过一个应用配置文件很容易地配置JSF应用。在这个文件里,可以注册应用中用到的JavaBeans,通过指定页面导航规则定义程序控制流程,注册自定义组件及执行其他配置工作等。
应用配置文件是一个XML文件,可以有好几种声明方式。最简单的方式是把它放到WEB-INF目录中,并且命名为faces-config.xml。应用配置文件的根元素是faces-config。下面是应用配置文件的框架:
- <?xml version="1.0"?>
- <!DOCTYPE faces-config PUBLIC
- "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
- "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
- <faces-config>
- </faces-config>
JSF应用的许多方面都可以通过应用配置文件来配置。在这里,我们主要关注如何注册JavaBeans(用于本章稍后的例子)。第15章会详细讲解应用配置文件。在一个JSP页面里,可以使用jsp:useBean动作告诉JSP容器,正在使用jsp:useBean动作的class特性里指定的JavaBean,如下所示:
- <jsp:useBean id="numberBean" class="ch02.NumberBean" scope="session"/>
这将告诉Web容器装入JavaBean类并且在JSP页面被调用时创建一个实例。jsp:useBean动作只需要在一个页面里声明,而在同一个应用的所有JSP页面里可用。在JSF应用中允许您做到这一点,而不是在应用配置文件中注册JavaBean。
使用<jsp:useBean>有一个缺点,如果一个页面在包含jsp:useBean动作的页面之前被调用,Web容器会抛出一个异常。这是因为这个页面试图使用一个尚未创建的JavaBean。而使用应用配置文件注册该JavaBean就不会有这个问题。
对于每个打算在应用配置文件里注册的JavaBean,使用faces-config元素中的managed-bean标记,在managed-bean元素中有下列子元素:
◆managed-bean-name 标记定义从JSP页面里引用JavaBean时使用的名称。
◆managed-bean-class元素 指明JavaBean类。
◆managed-bean-scope元素 定义JavaBean的作用域。
下面是managed-bean的一个例子:
- <managed-bean>
- <managed-bean-name>myBean</managed-bean-name>
- <managed-bean-class>myPackage.MyBean</managed-bean-class>
- <managed-bean-scope>session</managed-bean-scope>
- </managed-bean>
本例中的managed-bean元素定义了一个类型为myPackage.MyBean的JavaBean,在JSF应用的JSP页面里可以用名称myBean来引用它,它的作用域是session,表示开始一个用户会话时就创建这个bean的一个实例。
【编辑推荐】