Ajaxを使うJSF2のイベントの呼び出しタイミング

JSF2でAjaxに対応していろいろできるんですけど、そのときのメソッドの呼び出しタイミングについて調べてみたのでまとめておきます。
ここで、JSF標準とPrimeFacesのcommandButtonについてactionとactionListener、あとsetPropertyActionListener、それから処理後のレンダリングについて調べてます。


結論としては、

  1. actionListener
  2. setPropertyActionListener
  3. Action

の順に処理が行われ、最後にレンダリング用の値が取り出されます。


とりあえず更新確認用のoutputTextを置きます。

<h:outputText id="aft" value="#{myBean.aft}"/>


JSF標準については、次のようにボタンを置きます。

<h:commandButton value="てすと" action="#{myBean.action()}" 
                 actionListener="#{myBean.actListener()}" >
    <f:ajax execute="@this" render=":form:aft"/>
    <f:setPropertyActionListener
        value="#{myBean.hogeg}" target="#{myBean.hoges}"/>
</h:commandButton>

JSF標準のcommandButtonの場合、f:ajaxを使ってexecuteでパーシャルアップデートでポストする内容、renderで更新するコンポーネントを指定します。
setPropertyActionListenerは、そのとき設定されるプロパティの指定です。


PrimeFacesについては、次のようにボタンを置きます。

<p:commandButton value="てすと" action="#{myBean.action()}" 
                 process="@this" update=":form:aft"
                 actionListener="#{myBean.actListener()}" >
    <f:setPropertyActionListener 
        value="#{myBean.hogeg}" target="#{myBean.hoges}"/>
</p:commandButton>

PrimeFacesのcommandButtonはそのままajaxに対応しているので、processでパーシャルアップデートのポスト内容、updateで更新コンポーネントを指定します。


それぞれのメソッドの更新タイミングを見てみると、どちらも同じ感じで次のようになりました。

情報: -222126954:1364595680399:actList
情報: -222126954:1364595680399:getHogeg
情報: -222126954:1364595680399:setHoges
情報: -222126954:1364595680399:action
情報: -222126954:1364595680400:getAft

最初の数字は、ランダムな数字をリクエストごとに指定しています。すべて同じリクエスト内での処理呼び出しであることがわかります。
また、最初にactionListenerが呼び出されてからsetPropertyActionListenerの処理が行われ、actionが呼び出されます。
そして、最後に結果更新が行われます。おもしろいのは、最後の結果更新までひとつのリクエストであるということです。


特にsetPropertyActionListenerを使う場合には、この呼び出し順を意識しておく必要があります。


あと、JSFAjax処理を宣言的に記述できる、よくできたフレームワークだと実感してます。
Ajax処理を、手続きとしてではなく構造指定として記述できて、ビューと処理を統合的に扱えるフレームワークは、他にはないんじゃないでしょうか。
細かい処理や条件による制御は苦手なので、一般向けサイトでのAjax処理というのには向きませんが、業務アプリや管理画面などでは非常に便利だと思います。


xhtmlと処理Beanの全体をはりつけておきます。

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        Hello from Facelets
        <br />
        <h:form id="form">
            <p><h:outputText value="#{myBean.aft}"/></p>
<h:commandButton value="てすと" action="#{myBean.action()}" 
                 actionListener="#{myBean.actListener()}" >
    <f:ajax execute="@this" render=":form:aft"/>
    <f:setPropertyActionListener
        value="#{myBean.hogeg}" target="#{myBean.hoges}"/>
</h:commandButton>
<p:commandButton value="てすと" action="#{myBean.action()}" 
                 process="@this" update=":form:aft"
                 actionListener="#{myBean.actListener()}" >
    <f:setPropertyActionListener 
        value="#{myBean.hogeg}" target="#{myBean.hoges}"/>
</p:commandButton>
            <h:outputText id="aft" value="#{myBean.aft}"/>
        </h:form>
    </h:body>
</html>
@Named
@RequestScoped
public class MyBean implements Serializable{
    int rand;

    public int getRand() {
        return rand;
    }
    
    @PostConstruct
    public void init(){
        rand = new Random().nextInt();
    }
    
    
    public void setHoges(String s){
        System.out.printf("%d:%d:%s", rand, new Date().getTime(), "setHoges");
    }

    public String getHogeg(){
        System.out.printf("%d:%d:%s", rand, new Date().getTime(), "getHogeg");
        return "aa";
    }
    public String getAft(){
        System.out.printf("%d:%d:%s", rand, new Date().getTime(), "getAft");
        return "bb" + rand;
    }
    public void actListener(){
        System.out.printf("%d:%d:%s", rand, new Date().getTime(), "actList");
    }
    public void action(){
        System.out.printf("%d:%d:%s", rand, new Date().getTime(), "action");
    }

}