コンストラクタ
コンストラクタの基本
package view;
import java.io.Serializable;
import jakarta.annotation.PostConstruct;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
@Named
@ViewScoped
public class NextBean implements Serializable {
private static final long serialVersionUID = 1L;
// コンストラクタ
@PostConstruct
public void init() {
System.out.println("ビーンが初期化されました!");
}
}
「ViewScoped」としている場合は、最初のGET/POST遷移時に発火します。 本当にそうでしょうか? 別に嘘をついた訳ではありませんが、Jakartaフレームワークは『.xhtml』が起点です。
そのビュー内に『#{○○○Bean.name}』的な記述が1つも無かったらどうなると思いますか? 答えはクラスが初期化されないです! クラスが初期化されてないので、コンストラクタなんて発火するハズもありませんよね。 軽量思想のフレームワークですので、リソースの節約の観点から、バッキングビーンが呼ばれるまで仮のインスタンスを生成します。
しかし、前述の様に1つもバッキングビーンを呼び出す記述が『.xhtml』に無い場合は仮のままとなります。
これ結構な罠ですよ。 例えば、画面Aで入力した内容を画面Bが受取り、何か処理をするとしましょう。
画面BはAから情報を貰うだけなので、まさにコンストラクタが発火しない状態となります。
これ最も厄介なのが、画面Bが画面Cに遷移する際にコンストラクタが発火するんですよ。 え!ですよね。 実はこれ先ほど説明した様に画面Bのオブジェクトが『仮』の状態の時、他画面に遷移する場合は、
ライフサイクルが終了するのでオブジェクトを破棄する必要があります。 何と破棄するために真のオブジェクトを生成するのです。 で、その際にコンストラクタが発火すると。 ではどうするか?無理やり関係ない『#{○○○Bean.name}』を記述するか?
実はあります。便利な仕組みが!
※前章のサンプルの状態の方は既に設定されてます 『NextBean.java』は、上記サンプルに差し替えて下さい。 するとどうでしょう。 何と今度はコンストラクタが2回発火してしまいます。
コンストラクタをカスタムする
前回何故コンストラクタが2度発火したのでしょうか。
それはコンストラクタ関数に付与する『『@PostConstruct』』が原因です。 「.xhtml」内にバッキングビーンを呼び出す記述が無いため、以下を記述し強引にコンストラクタ関数を呼び出しました。
『f:event type="preRenderView" listener="#{nextBean.init}"』 このタイミングで真のおオブジェクトが生成され、更にコンストラクタが発火した状態です。
つまり『@PostConstruct』は不要です。 基本であるアノテーションが不要とは何とも不思議な話ですが、フレームワークの特性上絶対に初期化したい場合はこの構造が良さそうです。
ただしこれで一安心..ではありません。 実はビュースコープでは画面にPOST遷移でとどまっている内は画面内の値を保持し続けます。
しかし作成した初期化メソッド『init』はPOST遷移しても強引に呼ばれる状態です。 そうです。POSTで自画面に遷移しても初期化されてしまうのです! ではどうするか。
初期化されるから画面の情報が消失するのであって、初期化メソッドに飛び込んだタイミングでは情報を当然保持しています。
なのでその際に既に初期化が完了している場合は何もしなければ良いですよね。
package view;
import java.io.Serializable;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
@Named
@ViewScoped
public class NextBean implements Serializable {
private static final long serialVersionUID = 1L;
private boolean isFirstTiem = false;
// コンストラクタ
public void init() {
if (isFirstTiem) {
return;
}
System.out.println("ビーンが初期化されました!");
this.isFirstTiem = true;
}
}
初期化処理に毎回飛び込んでも『isFirstTiem』が常に値を保持しているので、その後の処理をスキップできますよね。
コンストラクタをカスタムする②
ではありません。
確かに問題は解決しましたが、全てのバッキングビーンに同じような処理を記述するのは面倒ですよね。
そこで親クラスを作成し、そこに面倒な処理をまとめて使いまわしましょう。
package view;
import java.io.Serializable;
public abstract class BaseBean implements Serializable {
private static final long serialVersionUID = 1L;
protected boolean isFirstTiem = false;
protected static final String SELF = null;
public void init() {
if (isFirstTiem) {
return;
}
// 子がオーバーライドする
initialize();
// ライフサイクル中は初期化しない
isFirstTiem = true;
}
// 子の初期化メソッド定義
protected abstract void initialize();
}
package view;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
@Named
@ViewScoped
public class NextBean extends BaseBean {
private static final long serialVersionUID = 1L;
@Override
public void initialize() {
System.out.println("ネクストビーンが初期化されました!");
}
}
親クラスの定義に『protected static final String SELF = null;』があるのを気が付きましたか。
実はPOST遷移のラスボスがいます!
それが遷移の指定方法なのですが、POST遷移は『.xhtml』名を記述すると思っていませんか?
これ他画面遷移であれば正しいのですが、自画面遷移の場合は《リダイレクト》と同じ扱いとなります。
もう分かりましたね。
【return SELF;】の方が圧倒的に『あ、自画面遷移ね。』って可読性が上がりますよね。





<f:metadata>
<f:event type="preRenderView" listener="#{nextBean.init}" />
</f:metadata>