normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

JavaEE7 な JSF 2.2 の ViewScoped を試してみる

今回はふと JavaEE を弄っていてちょっとはまってしまったポイントについて紹介する。JavaEE 有識者各位ならば J2EE 時代の XML 地獄を脱却するため、JavaEE5 からはアノテーションによる規約ベースの設定を重視する文化になったことはご存じだと思うが、特に JSF には以下の闇がある。

  • JSF 1.0(JavaEE5 時代):DI コンテナがなく(厳密には JBoss Seam が存在したが、Java EE の仕様に入っていなかった)、ManagedBean は faces-config.xmlXML ベースで設定
  • JSF 2.0(JavaEE6 時代):DI コンテナ(CDI)が導入されたが、JSF 2.0 のアノテーションCDIアノテーションが混在し、更に JSF の機能と CDI の機能に差があった
  • JSF 2.2(JavaEE7 時代):JavaEE6 時代の問題を大体解決(原則 CDI 側に寄せた)したが、若干名残あり

特に、JavaEE6 時代の闇については 達人プログラマーを目指して > 大混乱に陥っているJavaEE 6のアノテーションに関する使い分けについて達人プログラマーを目指して > Java EE6環境でJSF2を使う場合はCDIのBeanを管理Beanとして使う方がよい が詳しい。

今回は JSF 2.2 より CDI compatible @ViewScoped が存在するので、そちらを確認してみる。

サンプルコード

ManagedBean 相当の Java ファイルと画面相当 xhtml ファイルは以下となる。 MessageAction.java の ①~④のアノテーションを組み合わせて検証する。

  • MessageAction.java
package mysample.app.action;

import java.io.Serializable;
//① import javax.faces.view.ViewScoped;
//② import javax.faces.bean.ManagedBean;
//③ import javax.faces.bean.ViewScoped;
//④ import javax.inject.Named;

//@ManagedBean
@ViewScoped
@Named
public class MessageAction implements Serializable {

    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String doAction() {
        System.out.println("message at MessageAction#doAction = " + message);
        return null;
    }
}
<?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://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:p="http://xmlns.jcp.org/jsf/passthrough">
    <h:head>
        <title>My Sample</title>
        <style type="text/css">
            .strike{
                text-decoration: line-through;
            }
        </style>            
    </h:head>
    <h:body>
        <h2>Scope Confrim</h2>
        <h:messages layout="table" styleClass="alert" />
        <h:form>
            <h:panelGrid columns="2">
                <h:outputLabel value="Title: "/>
                <h:inputText value="#{messageAction.message}" p:placeholder="please input text" />
                <h:commandButton value="DoAction" action="#{messageAction.doAction()}" />
            </h:panelGrid>
        </h:form>
    </h:body>
</html>

上記のうち、p:placeholder と javax.faces.view.ViewScoped は JSF 2.2 からの新機能だ。こちらのコードを利用した実行結果は以下となる。

  • ①+④:「情報: message at MessageAction#doAction = <入力値>」が出力
  • ①+②:「情報: message at MessageAction#doAction = null」が出力
  • ②+③:「情報: message at MessageAction#doAction = <入力値>」が出力
  • ③+④:「情報: message at MessageAction#doAction = null」が出力

なんでこうなるの?

冒頭で語った JSF の闇に記載した通り、JSF のライフサイクルと CDI のライフサイクルのどちらを利用するかで必要なアノテーションの組み合わせが異なるためだ。

javax.faces.view.ViewScoped & javax.inject.Named は CDI を利用したライフサイクルになる。以下に javax.faces.view.ViewScoped に対するコメントを抜粋する。

import javax.faces.view.ViewScoped;

When this annotation, along with javax.inject.Named is found on a class, the runtime must place the bean in a CDI scope such that it remains active as long as NavigationHandler.handleNavigation(javax.faces.context.FacesContext, java.lang.String, java.lang.String) does not cause a navigation to a view with a viewId that is different than the viewId of the current view. Any injections and notifications required by CDI and the Java EE platform must occur as usual at the expected time.


また、javax.faces.bean.ViewScoped & javax.faces.bean.ManagedBean の場合は JSF を利用したライフサイクルになる。以下に javax.faces.bean.ViewScoped に対するコメントを抜粋する。

import javax.faces.beans.ViewScoped;

When this annotation, along with ManagedBean is found on a class, the runtime must act as if a <managed-bean-scope>view<managed-bean-scope> element was declared for the corresponding managed bean.

更に、上記で検証した通り JSF 向けのアノテーションCDIアノテーションを組み合わせた場合はデータバインディングがうまく動かない点に注意が必要だ。
JSFCDI を利用する際の問題は、他のスコープについてのアノテーション(リクエストスコープ、セッションスコープ、アプリケーションスコープ)にも存在するため、これらのアノテーションの使い分けに注意してほしい。