事件处理

本文介绍了JavaServer Faces (JSF)中的事件处理机制,包括动作事件、即时事件、值变事件和Phase事件。通过示例讲解了如何使用这些事件进行程序设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

4. 事件处理

JSF的事件模型提供一个近似的桌面GUI事件方式,让熟悉GUI设计的人员也

能快速上手Web程序设计。

4.1 动作事件

JSF支持事件处理模型,虽然由于HTTP本身无状态(stateless)的特性,使

得这个模型多少有些地方仍不太相同,但JSF所提供的事件处理模型已足以让一

些传统GUI程序的设计人员,可以用类似的模型来开发程序。

1.2简单的导航中,我们根据动作方法(action method)的结果来决定要导

向的网页,一个按钮绑定一个方法,这样的作法实际上是JSF所提供的简化的事

件处理程序,在按钮上使用action绑定一个动作方法(action method),实际上JSF

会为其自动产生一个「预定义的ActionListener」来处理事件,并根据其传回值来

决定导向的页面。

如果您需要使用同一个方法来应付多种事件来源,并想要取得事件来源的相

关信息,您可以让处理事件的方法接收一个javax.faces.event.ActionEvent事件参

数,例如:

UserBean.java

package onlyfun.caterpillar;

import javax.faces.event.ActionEvent;

public class UserBean {

private String name;

private String password;

private String errMessage;

private String outcome;

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 void verify(ActionEvent e) {

if(!name.equals("justin") || !password.equals("123456")) {

errMessage = "名称或密码错误" + e.getSource();

outcome = "failure";

}

else {

outcome = "success";

}

}

public String outcome() {

return outcome;

}

}

在上例中,我们让verify方法接收一个ActionEvent对象,当使用者按下按钮,

会自动产生ActionEvent对象代表事件来源,我们故意在错误信息之后加上事件来

源的字符串描述,这样就可以在显示错误信息时一并显示事件来源描述。

为了提供ActionEvent的存取能力,您的index.jsp可以改写如下:

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=Big5"%>

<html>

<head>

<title>第一个JSF程序</title>

</head>

<body>

<f:view>

<h:form>

<h3>请输入您的名称</h3>

<h:outputText value="#{user.errMessage}"/>

<p>名称: <h:inputText value="#{user.name}"/>

<p>密码: <h:inputSecret value="#{user.password}"/>

<p><h:commandButton value="送出" actionListener="#{user.verify}"

action="#{user.outcome}"/>

</h:form>

</f:view>

</body>

</html>

主要改变的是按钮上使用了actionListener属性,这种方法可以使用一个

ActionListenerJSF会先检查是否有指定的actionListener,然后再检查是否指定

了动作方法并产生预定义的ActionListener,并根据其传回值导航页面。

如果您要注册多个ActionListener,例如当使用者按下按钮时,顺便在记录文

件中增加一些记录信息,您可以实现javax.faces.event.ActionListener,例如:

LogHandler.java

package onlyfun.caterpillar;

import javax.faces.event.ActionListener;

....

public class LogHandler implements ActionListener {

public void processAction(ActionEvent e) {

// 处理Log

}

}

VerifyHandler.java

package onlyfun.caterpillar;

import javax.faces.event.ActionListener;

....

public class VerifyHandler implements ActionListener {

public void processAction(ActionEvent e) {

// 处理验证

}

}

这么一来,您就可以使用<f:actionListener>标签向组件注册事件,例如:

<h:commandButton value="送出" action="#{user.outcome}">

<f:actionListener type="onlyfun.caterpillar.LogHandler"/>

<f:actionListener type="onlyfun.caterpillar.VerifyHandler"/>

</h:commandButton>

<f:actionListener>会自动产生type所指定的对象,并呼叫组件的

addActionListener()方法注册Listener

4.2 即时事件

所谓的即时事件(Immediate Events),是指JSF视图组件在取得请求中该取

得的值之后,即立即处理指定的事件,而不再进行后续的转换器处理、验证器处

理、更新模型值等流程。

JSF的事件模型中之所以会有所谓即时事件,是因为Web应用程序的先天

特性不同于GUI程序,所以JSF的事件方式与GUI程序的事件方式仍有相当程度的

不同,一个最基本的问题正因为HTTP无状态的特性,使得Web应用程序天生就

无法直接唤起服务器端的特定对象。

所有的对象唤起都是在服务器端执行的,至于该唤起什么对象,则是依一个

基本的流程:

重建视图(Restore View

依客户端传来的session数据或服务器端上的session数据,重建JSF视图组件。

套用请求值(Apply Request Values

JSF视图组件各自获得请求中的属于自己的值,包括旧的值与新的值。

执行验证(Process Validations

转换为对象并进行验证。

更新模型值(Update Model Values

更新Bean或相关的模型值。

唤起应用程序(Invoke Application

执行应用程序相关逻辑。

绘制响应页面(Render Response

对先前的请求处理完之后,产生页面以反应客户端执行结果。

对于动作事件(Action Event)来说,组件的动作事件是在套用请求值阶段

就生成ActionEvent对象了,但相关的事件处理并不是马上进行,ActionEvent

先被排入队列,然后必须再通过验证、更新阶段,之后才处理队列中的事件。

这样的流程对于按下按钮然后执行后端的应用程序来说不成问题,但有些事

件并不需要这样的流程,例如只影响页面的事件。

举个例子来说,在表单中可能有使用者名称、密码等栏目,并提供有一个地

区选项按钮,使用者可以在不填写名称、密码的情况下,就按下地区选项按钮,

如果依照正常的流程,则会进行验证、更新模型值、唤起应用程序等流程,但显

然的,使用者名称与密码是空白的,这会引起不必要的错误。

您可以设定组件的事件在套用请求值之后立即被处理,并跳过后续的阶段,

直接进行页面绘制以响应请求,对于JSFinputcommand组件,都有一个

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:form>

</body>

</html>

</f:view>

这是一个可以让使用者决定使用语系的示范,最后一个commandButton组件

被设定了immediate属性,当按下这个按钮后,JSF套用请求值之后会立即处理指

定的actionListener,而不再进行验证、更新模型值,简单的说,就这个程序来说,

您在输入栏目与密码栏目中填入的值,不会影响您的user.nameuser.password

基于范例的完整起见,我们列出这个程序Bean对象及faces-config.xml

UserBean.java

package onlyfun.caterpillar;

import javax.faces.event.ActionEvent;

public class UserBean {

private String locale = "en";

private String name;

private String password;

private String errMessage;

public void changeLocale(ActionEvent e) {

if(locale.equals("en")) locale = "zh_TW";

else locale = "en";

}

public String getLocale() {

if (locale == null) {

locale = "en";

}

return 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>

onlyfun.caterpillar.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

Text中设定的是「中文」转换为Java Unicode Escape格式的结果,另一个信

息资源文件的内容则是英文信息的翻译而已,其转换为Java Unicode Escape格式

结果如下:

messages_zh_TW.properties

titleText=JSF\u793a\u7bc4

hintText=\u8acb\u8f38\u5165\u540d\u7a31\u8207\u5bc6\u78bc

nameText=\u540d\u7a31

passText=\u5bc6\u78bc

commandText=\u9001\u51fa

Text=English

welcome.jsp就请自行设计了,程序的画面如下:

4.3 值变事件

如果使用者改变了JSF输入组件的值后提交表单,就会发生值变事件(Value

Change Event),这会丢出一个javax.faces.event.ValueChangeEvent对象,如果您

想要处理这个事件,有两种方式,一是直接设定JSF输入组件的

valueChangeListener属性,例如:

<h:selectOneMenu value="#{user.locale}"

onchange="this.form.submit();"

valueChangeListener="#{user.changeLocale}">

<f:selectItem itemValue="zh_CN" itemLabel="Chinese"/>

<f:selectItem itemValue="en" itemLabel="English"/>

</h:selectOneMenu>

为了模拟GUI中选择了选单项目之后就立即发生反应,我们在onchange属性

中使用了JavaScript,其作用是在选项项目发生改变之后,立即提交表单,而不

用按下提交按钮;而valueChangeListener属性所绑定的user.changeLocale方法必须

接受ValueChangeEvent对象,例如:

UserBean.java

package onlyfun.caterpillar;

import javax.faces.event.ValueChangeEvent;

public class UserBean {

private String locale = "en";

private String name;

private String password;

private String errMessage;

public void changeLocale(ValueChangeEvent event) {

if(locale.equals("en")) locale = "zh_CN";

else locale = "en";

}

public void setLocale(String locale) {

this.locale = locale;

}

public String getLocale() {

if (locale == null) {

locale = "en";

}

return 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";

}

}

}

另一个方法是实现javax.faces.event.ValueChangeListener接口,并定义其

processValueChange()方法,例如:

SomeListener.java

package onlyfun.caterpillar;

....

public class SomeListener implements ValueChangeListener {

public void processValueChange(ValueChangeEvent event) {

....

}

....

}

然后在JSF页面上使用<f:valueChangeListener>标签,并设定其type属性,例如:

<h:selectOneMenu value="#{user.locale}" onchange="this.form.submit();">

<f:valueChangeListener type="onlyfun.caterpillar.SomeListener"/>

<f:selectItem itemValue="zh_CN" itemLabel="Chinese"/>

<f:selectItem itemValue="en" itemLabel="English"/>

</h:selectOneMenu>

下面这个页面是对4.2即时事件中的范例程序作一个修改,将语言选项改以下拉

式选单的选择方式呈现,这必须配合上面提供的UserBean类来使用:

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>

<h:selectOneMenu value="#{user.locale}"

immediate="true"

onchange="this.form.submit();"

valueChangeListener="#{user.changeLocale}">

<f:selectItem itemValue="zh_CN" itemLabel="Chinese"/>

<f:selectItem itemValue="en" itemLabel="English"/>

</h:selectOneMenu>

<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:form>

</body>

</html>

</f:view>

4.4 Phase事件

4.2即时事件中我们提到,JSF的请求执行到反应,完整的过程会经过六个

阶段:

重建视图(Restore View

依客户端传来的session数据或服务器端上的session数据,重建JSF视图组件。

套用请求值(Apply Request Values

JSF视图组件各自获得请求中的属于自己的值,包括旧的值与新的值。

执行验证(Process Validations

转换为对象并进行验证。

更新模型值(Update Model Values

更新Bean或相关的模型值。

唤起应用程序(Invoke Application

执行应用程序相关逻辑。

绘制响应页面(Render Response

对先前的请求处理完之后,产生页面以反应客户端执行结果。

在每个阶段的前后会引发javax.faces.event.PhaseEvent,如果您想尝试在每个

阶段的前后捕捉这个事件,以进行一些处理,则可以实现

javax.faces.event.PhaseListener,并向javax.faces.lifecycle.Lifecycle登记这个

Listener,以在适当的时候通知事件的发生。

PhaseListener有三个必须实现的方法getPhaseId()beforePhase()

afterPhase(),其中getPhaseId()传回一个PhaseId对象,代表Listener想要被通知的

时机,可以设定的时机有:

PhaseId.RESTORE_VIEW

PhaseId.APPLY_REQUEST_VALUES

PhaseId.PROCESS_VALIDATIONS

PhaseId.UPDATE_MODEL_VALUES

PhaseId.INVOKE_APPLICATION

PhaseId.RENDER_RESPONSE

PhaseId.ANY_PHASE

其中PhaseId.ANY_PHASE指的是任何的阶段转换时,就进行通知;您可以

beforePhase()afterPhase()中编写阶段前后分别想要处理的动作,例如下面这

个简单的类会列出每个阶段的名称:

ShowPhaseListener.java

package onlyfun.caterpillar;

import javax.faces.event.PhaseEvent;

import javax.faces.event.PhaseId;

import javax.faces.event.PhaseListener;

public class ShowPhaseListener implements PhaseListener {

public void beforePhase(PhaseEvent event) {

String phaseName = event.getPhaseId().toString();

System.out.println("Before " + phaseName);

}

public void afterPhase(PhaseEvent event) {

String phaseName = event.getPhaseId().toString();

System.out.println("After " + phaseName);

}

public PhaseId getPhaseId() {

return PhaseId.ANY_PHASE;

}

}

编写好PhaseListener后,我们可以在faces-config.xml中向Lifecycle进行注册:

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>

<lifecycle>

<phase-listener>

onlyfun.caterpillar.ShowPhaseListener

</phase-listener>

</lifecycle>

......

</faces-config>

您可以使用这个简单的类,看看在请求任一个JSF画面时所显示的内容,借

此了解JSF每个阶段的流程变化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值