12.3. 生命周期阶段侦听器

在通常的生命周期逻辑中,用户加入自己的逻辑的时机主要是处理验证阶段的校验器或转换器和调用应用程序阶段的动作事件方法,这些代码都是在postback时才有可能被调用。但我们往往需要在其他时机加入一些处理,最典型的场景是,假如我们需要在首次请求时根据URL的GET方式参数(例如www.abc.com?id=123)作一些处理,由于首次请求时生命周期只经过恢复视图阶段和渲染响应阶段,那么这个处理逻辑应该放在哪里呢?答案是放在生命周期阶段侦听器中。

12.3.1. 应用范围内的侦听器

OperaMasks引擎会在每个生命周期阶段的开始和结束时分别广播生命周期阶段事件。通过向OperaMasks引擎注册生命周期监听器,就可以监听到这些事件并触发处理。在这里我们先看看在OperaMasks中使用生命周期阶段监听器的第一种方式。

首先,通过实现javax.faces.event.PhaseListener接口定义一个生命周期侦听器。这个接口提供三个方法:

public interface PhaseListener extends EventListener, Serializable {
    1
    public void afterPhase(PhaseEvent event);
    2
    public void beforePhase(PhaseEvent event);
    3
    public PhaseId getPhaseId();

}
    
1

实现此方法加入在生命周期阶段结束时的处理逻辑

2

实现此方法加入在生命周期阶段开始时的处理逻辑

3

在此方法中返回这个侦听器所侦听的生命周期阶段,例如return PhaseId.RESTORE_VIEW; 若返回PhaseId.ANY_PHASE将侦听所有生命周期阶段事件。

下面看看一个简单的生命周期侦听器如何去实现这个接口:

package test;
import javax.faces.event.*;
public class MyListener implements PhaseListener {
    public void afterPhase(PhaseEvent event) {
        System.out.println(event.getPhaseId().toString() + " afterPhase");
        if (event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
            System.out.println("OperaMasks 生命周期结束.....");
        }
    }
    public void beforePhase(PhaseEvent event) {
        if (event.getPhaseId() == PhaseId.RESTORE_VIEW) {
            System.out.println("OperaMasks 生命周期开始.....");
        }
        System.out.println(event.getPhaseId().toString() + " beforePhase");
    }
    public PhaseId getPhaseId() {
        return PhaseId.ANY_PHASE;
    }

}

然后,为了让侦听器生效,需要在faces-config.xml中注册此侦听器。

<lifecycle>
    <phase-listener>test.MyListener</phase-listener>
</lifecycle>   

在OperaMasks中,定义生命周期阶段侦听器还有另一种更简洁的方式。

首先,我们仍然可以通过实现javax.faces.event.PhaseListener接口来定义生命周期阶段侦听器。但只需要为这个侦听器类加上@PhaseListener注解,这个侦听器即可生效,而无需在faces-config.xml中配置。

但应注意,OperaMasks不支持把一个LiteBean使用@PhaseListener注册为生命周期侦听器,即使这个LiteBean实现了PhaseListener接口。

12.3.2. 页面范围内的侦听器

上述方法中定义的侦听器会监听整个应用范围内的所有请求。如果你只想监听特定页面内的生命周期阶段,可以使用一种更小范围的phaseListener。你可以在<f:view>内添加beforePhaseListener和afterPhaseListener属性来实现

监听这个页面内的阶段事件。这两个标签的值应该是用EL表达式绑定的一个后台方法,这个方法的签名必须是"void phaseListener(javax.faces.event.PhaseEvent event);"。具体用法如下:

<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core"
    beforePhaseListener="#{loginBean.beforePhaseInPage }" afterPhaseListener="#{loginBean.afterPhaseInPage }" renderKitId="AJAX" >
 ... ...
</f:view>

或者,你也可以在LiteBean中使用@BeforePhase和@AfterPhase注解直接定义生命周期阶段监听方法:

    @AfterPhase 
    public void afterPhase(PhaseEvent event){
        log = log + "\n" + event.getPhaseId().toString() + "阶段结束";
    }
    
    @BeforePhase 
    public void beforePhase(PhaseEvent event){
        log = log + "\n" + event.getPhaseId().toString() + "阶段开始";
    }

这两种用法是等价的,它们所指定的监听方法都会在页面进入到每个生命周期阶段之前和之后被调用,除了恢复视图阶段(生命周期的第一阶段)之外。

另外,在页面的<f:view>中写属性的方式是比在LiteBean中使用注解的优先级高的,也就是说,如果一个页面的<f:view>中指定了phaseListener,而这个页面对应的LiteBean又有@AfterPhase 和@BeforePhase

注解的话,只有第一种方式的监听方法会被调用,而第二种方式的监听方法将会被忽略。

12.3.3. @BeforeRender和@AfterRender

由于渲染响应阶段在首次请求与postback中都会执行,而且此阶段的BeforePhase事件是页面被渲染前对构件树进行修改的最后机会。很多情况下我们都会在这个阶段增加监听方法。

因此OperaMasks为这个阶段定义了专门的侦听注解:使用@BeforeRender和@AfterRender注解的方法可以分别侦听渲染响应阶段之前和之后广播的阶段事件。

这个两个方法对应的方法签名分别为:"void beforeRender(boolean isPostback);" 和 "void afterRender();"

使用这两个注解相当于在@BeforePhase和@AfterPhase注解方法中加入了类似的判断:

if (event.getPhaseId().equals(PhaseId.RENDER_RESPONSE)){
   // Do something in RENDER_RESPONSE phase         
}

同样,如果页面的<f:view>已经指定了生命周期监听方法,@BeforeRender和@AfterRender注解的方法也会被忽略。