实时事件
所谓的实时事件(Immediate Events),是指JSF视图组件在取得请求中该取得的值之后,即立即处理指定的事件,而不再进行后续的转换器处理、验证器处理、更新模型值等流程。
在JSF的事件模型中会有所谓实时事件,导因于Web应用程序的先天特性不同于GUI程序,所以JSF的事件模式与GUI程序的事件模式仍有相当程度的不同,一个最基本的问题正因为HTTP无状态的特性,使得Web应用程序天生就无法直接唤起伺服端的特定对象。所有的对象唤起都是在伺服端执行的,至于该唤起什么对象,则是依一个基本的流程:
- 回复画面(Restore View)
对于选择的页面如果是初次浏览则建立新的组件树。如果是会话阶段,会从使用者端或服务器端的数据找寻数据以回复每个组件的状态并重建组件树,如果不包括请求参数,则直接跳过接下来的阶段直接绘制响应。
- 套用申请值(Apply Request Values)
每个组件尝试从到来的请求中找寻自己的参数并更新组件值,在这边会触发ActionEvent,这个事件会被排入队列中,然后在唤起应用程序阶段之后才会真正由事件处理者进行处理。
然而对于设定immeduate为true的命令(Commamnd)组件来说,会立即处理事件并跳过之后的阶段直接绘制响应,而对于设定immediate为true的输入(Input)组件,会马上进行转换验证并处理值变事件,之后跳过接下来的阶段,直接绘制响应。
- 执行验证(Process Validations)
进行转换与验证处理,如果验证错误,则会跳过之后的阶段,直接绘制响应,结果是重新呼叫同一页绘制结果。
- 更新模型值(Update Model Values)
更新每一个与组件绑定的backing bean或模型对象。
- 唤起应用程序(Invoke Application)
处理动作事件,并进行后端应用程序逻辑。
- 绘制回应(Render Response)
使用绘制器绘制页面。对于动作事件(Action Event)来说,组件的动作事件是在套用请求值阶段就生成ActionEvent对象了,但相关的事件处理并不是马上进行,ActionEvent会先被排入队列,然后必须再通过验证、更新模式值阶段,之后才处理队列中的事件。
这样的流程对于按下按钮然后执行后端的应用程序来说不成问题,但有些事件并不需要这样的流程,例如只影响画面的事件。举个例子来说,在窗体中可能有使用者名称、密码等字段,并提供有一个地区选项按钮,使用者可以在不填下按钮的情况下,就按下地区选项按钮,如果依照正常的流程,则会进行验证、更新模型值、唤起应用程序等流程,但显然的,使用者名称与密码是空白的,这会引起不必要的错误。
您可以设定组件的事件在套用请求值之后立即被处理,并跳过后续的阶段,直接进行画面绘制以响应请求,对于JSF的input与command组件,都有一个immediate属性可以设定,只要将其设定为true,则指定的事件就成为立即事件。
一个例子如下:
index.jsp
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <%@page contentType="text/html;charset=UTF8"%> <f:view locale="#{user.locale}"> <f:loadBundle basename="messages" var="msgs" /> <html> <head> <title><h:outputText value="#{msgs.titleText}" /></title> </head> <body> <h:form> <h3> <h:outputText value="#{msgs.hintText}" /> </h3> <h:outputText value="#{msgs.nameText}" />: <h:inputText value="#{user.name}" /> <p> <h:outputText value="#{msgs.passText}" /> : <h:inputSecret value="#{user.password}" /> <p> <h:commandButton value="#{msgs.commandText}" action="#{user.verify}" /> <h:commandButton value="#{msgs.Text}" immediate="true" actionListener="#{user.changeLocale}"> </h:commandButton> </h:form> </body> </html> </f:view>
这是一个可以让使用者决定使用语系的示范,最后一个commandButton组件被设定了immediate属性,当按下这个按钮后,JSF套用请求值之后会立即处理指定的actionListener,而不再进行验证、更新模型值,简单的说,就这个程序来说,您在输入字段与密码字段中填入的值,不会影响您的user.name与user.password。
UserBean.java
package wsz.ncepu; import javax.faces.event.ActionEvent; public class UserBean { private String locale; private String name; private String password; private String errMessage; public void changeLocale(ActionEvent e) { if ("en".equals(locale)) locale = "zh_CN"; else locale = "en"; } public String getLocale() { return locale; } public void setLocale(String locale) { this.locale = locale; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setPassword(String password) { this.password = password; } public String getPassword() { return password; } public void setErrMessage(String errMessage) { this.errMessage = errMessage; } public String getErrMessage() { return errMessage; } public String verify() { if (!name.equals("justin") || !password.equals("123456")) { errMessage = "错误"; return "failure"; } else { return "success"; } } }
faces-config.xml
<?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> <navigation-rule> <from-view-id>/pages/index.jsp</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/pages/welcome.jsp</to-view-id> </navigation-case> <navigation-case> <from-outcome>failure</from-outcome> <to-view-id>/pages/index.jsp</to-view-id> </navigation-case> </navigation-rule> <managed-bean> <managed-bean-name>user</managed-bean-name> <managed-bean-class> wsz.ncepu.UserBean </managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> </faces-config>
讯息资源文件的内容则是如下:
messages_en.properties
titleText=JSF Demo hintText=Please input your name and password nameText=name passText=password commandText=Submit Text=\u4e2d\u6587
messages_zh_CN.properties
titleText=JSF\u793A\u8303 hintText=\u8BF7\u8F93\u5165\u7528\u6237\u540D\u5BC6\u7801 nameText=\u540D\u79F0 passText=\u5BC6\u7801 commandText=\u9001\u51FA Text=English
程序的画面如下:
如果加上phase事件中代码,会发现immediate="true"时,后台打印的输出
16:52:01,939 INFO [STDOUT] Before RESTORE_VIEW 1 16:52:01,970 INFO [STDOUT] After RESTORE_VIEW 1 16:52:01,970 INFO [STDOUT] Before APPLY_REQUEST_VALUES 2 16:52:01,970 INFO [STDOUT] After APPLY_REQUEST_VALUES 2 16:52:01,970 INFO [STDOUT] Before RENDER_RESPONSE 6 16:52:02,010 INFO [STDOUT] After RENDER_RESPONSE 6
不加immediate="true"时 后台打印的输出
16:50:52,044 INFO [STDOUT] Before RESTORE_VIEW 1 16:50:52,065 INFO [STDOUT] After RESTORE_VIEW 1 16:50:52,065 INFO [STDOUT] Before APPLY_REQUEST_VALUES 2 16:50:52,075 INFO [STDOUT] After APPLY_REQUEST_VALUES 2 16:50:52,075 INFO [STDOUT] Before PROCESS_VALIDATIONS 3 16:50:52,075 INFO [STDOUT] After PROCESS_VALIDATIONS 3 16:50:52,075 INFO [STDOUT] Before UPDATE_MODEL_VALUES 4 16:50:52,075 INFO [STDOUT] After UPDATE_MODEL_VALUES 4 16:50:52,075 INFO [STDOUT] Before INVOKE_APPLICATION 5 16:50:52,075 INFO [STDOUT] After INVOKE_APPLICATION 5 16:50:52,075 INFO [STDOUT] Before RENDER_RESPONSE 6 16:50:52,105 INFO [STDOUT] After RENDER_RESPONSE 6