SAStrutsとStrutsのアクションのスコープの違い

SAStrutsStrutsはアクションのコンポーネント
スコープが異なっているので注意しなければならない。


アクションのスコープの違い

Struts singleton
SAStruts request

Strutsのアクションが持つフィールド

複数のスレッド間で共有される一方、
冗長化構成のマシン間では共有されない。
このようなクセがあるために、使用を避ける、
もしくは、慎重に使う必要がありました。
(使用禁止にしていたプロジェクトも多いと思われる)

SAStrutsのアクションのフィールド

他のスレッドに影響を及ぼさないrequestスコープなので、
気軽にどんどん使用することができる。


ちなみに、アクションのフィールドをpublicフィールドとして
宣言しておくと、次のようにJSP側のEL式で簡単に呼び出すことができる。


HogeAction.java

package tutorial.action;
import org.seasar.struts.annotation.Execute;

public class HogeAction {

    public String xxx;

    @Execute(validator = false)
    public String fuga() {
        xxx = "Hello World!";
        return "fuga.jsp";
    }
}


fuga.jsp

<html>
<body>
  ${f:h(xxx)}
</body>
</html>


http://localhost:8080/sa-struts-tutorial/hoge/fuga の出力結果

Hello World!

実行メソッドのネーミングルールを検討してみる

従来のStrutsであれば、「通常のアクション」が XxxActionだとすると、
「画面初期化用のアクション」は InitXxxAction とするのが慣習でした。


SAStrutsでは従来のStrutsと異なり1つのアクションが
複数の実行メソッドを持つことができます。


このアーキテクチャの違いに着目し、
実際のプロジェクトで使うことを想定して
SAStruts用の実行メソッドのネーミングルールを検討してみました。


SAStrutsでは従来の「画面初期化用のアクション」はアクション内の
実行メソッドに対応します。では、このメソッドのネーミングルールをどうすべきか?
ズバリ、対応するJSP名(JSPファイル名の拡張子を取り除いた名前)が
シンプルで良いんじゃないかと。具体的には以下のような感じです。

    @Execute(validator = false)
    public String add() {
        ・・・ 画面に表示するデータを準備する ・・・
        return "add.jsp";
    }


以前、ひがさんがWEB+DB PRESS vol.36の記事で以下のようなことをおっしゃっていました。

業務ロジックは、主に2つのパターンに分けられます。
- 画面に表示するデータを準備する
- 画面に入力されたデータを処理する


この『画面に表示するデータを準備する』メソッドを
SASturtsではJSP名に対応させるようにすれば、
超シンプルではないでしょうか。
Teedaではinitialize()とprerender()に相当するメソッド)
名前があった方がわかりやすいので、ひとまず、viewメソッドと名付けておく。
jspメソッドとかの方がいいかな。)


また、『画面に入力されたデータを処理する』は、
do で始まるメソッド(通称: doメソッド)にすると分かり易いと考えています。
(doメソッドでリダイレクトするケースはほとんどないので、URLをdoで汚す心配はなさそうです。)

    @Execute(input = "add.jsp")
    public String doAdd() {
        ・・・ 画面に入力されたデータを処理する ・・・
        return add(); 
    }


doメソッドの return文は、return "add.jsp"; のようにしても動くことは動きます。
しかし、doメソッドの最後に『画面に表示するデータを準備する』メソッドを呼び出すスタイルをオススメします。なぜなら、メソッドの責務を明確にして、DRYなコードを書くことができるからです。

WEB+DB PRESS Vol.43

WEB+DB PRESS Vol.43」の献本を頂きました。
ありがとうございます! > 技術評論社のウェブDBプレス編集部の方々


ササッ、と感想を書いてみる。

入門Scala オブジェクト指向関数型言語Java VM上で融合した次世代高速スクリプト言語

Vol.43において個人的に最も興味深い記事。

Scalaは、JavaRubyの美味しいとこ取りしたような印象。
社内のRuby好きな人も高い評価をしていました。


SpringJDBCを使ってDBにアクセスするDAOをScalaで実装していたので、Seasarとの連携を試したくなりました。

詳解! PostgreSQL 3.3

パッと見ただけで、内容の濃さが伝わってくる。
性能向上に関するトピックを実測値を踏まえて紹介。
つくづく「速さは力なり」と思う。

Eclipse 現場で速攻使えるテクニック 開発リズム 向上大作戦

今度、ある企業さん向けにセミナーをやる時に、
実演のリズムを良くするために活用させてもらおう。

よりスマートな画面遷移?

先日、社内のRailsな人にSAStrutsのことを軽く説明する機会がありました。


以前のエントリーに書いた『画面に表示するデータを準備する』メソッドの
名前は遷移先のjspファイル名にあわせる、という非公式の私のお作法を説明した後に、
以下のようなコードを見てもらったのですが、どうも気に入らないと。

    @Execute(validator = false)
    public String hoge() {
        return "hoge.jsp";
    }


理由は次のとおり。

  • 遷移先のJSPファイル名とメソッド名をマッピングさせるお作法は、メソッド名と戻り値に同じ文字列が出てくるのでDRYっぽくない
  • せっかくJavaを使っているのに、戻り値がタイプセーフじゃないのはダサい


うーん、なるほど。一理ある。
ということで、以下のような感じで記述できる
インターセプター(ForwardByMethodNameInterceptor)を作ってみました。


このインターセプターを適用すると、
先ほどのコードは以下のように書き換えることが出来ます。
(一応、社内のRailsな人も納得でした。)

    @Execute(validator = false)
    public String hoge() {
        return Forward.BY_MEHTOD_NAME;
    }


実行メソッドの戻り値がForward.BY_MEHTOD_NAMEだったら、
戻り値を「メソッド名 + ".jsp"」に差し替えるのが
ForwardByMethodNameInterceptor の機能です。
Forward.BY_MEHTOD_NAME はString型の定数です。
(このキーワードはより分かりやすいものに見直したいところ。)


元々、SAStrutsはURLとメソッドはCoCによりマッピングできるが、
これにより URLとメソッドとJSPファイルが簡単なルールにより
マッピングできることになります。


ForwardByMethodNameInterceptor 導入の
メリット・デメリットについて検討してみました。


【メリット】

  • よりタイプセーフなコーディングが可能
  • メンテナンス性の向上(JSPファイル名の変更があった時に、戻り値も一緒に修正する手間を省けるため)
  • JSPファイルとメソッドのマッピングが明確となりソースコードの可読性UP
  • 『画面表示用データ準備』メソッドと『画面入力データ処理』メソッドの責務が分離しやすくなる
  • JSPファイル名から機械的に実行メソッドの雛形が作成可能となる
  • フレームワークが押し付ける機能ではない。使いたい人が使えばよい自由さがある


【デメリット】

  • やや黒魔術的という意味において、ソースコードの可読性が下がる
  • 覚えることが少し増えて、学習コストが上がる
  • フレームワークの肥大化につながる


作成したソースコードは以下のとおり。


ForwardByMethodNameInterceptor.java

public class DefaultRoutingInterceptor extends AbstractInterceptor {

    /**
     * 遷移先のパスが Forward.BY_MEHTOD_NAME だったら、
     * 戻り値を「メソッド名 + ".jsp"」に書き換えます。
     */
    public Object invoke(MethodInvocation invocation) throws Throwable {

        Object ret = invocation.proceed();

        if (isExecuteMethod(invocation.getMethod())) {
            String navitationPath = ret.toString();
            if (Forward.BY_MEHTOD_NAME.equals(navitationPath)) {
                return invocation.getMethod().getName() + ".jsp";
            }            
        }
        
        return ret;
    }

    protected boolean isExecuteMethod(Method method) {
        Execute execute = method.getAnnotation(Execute.class);

        if (execute == null
            || method.getParameterTypes().length > 0
            || method.getReturnType() != String.class) {
            return false;
        }

        return true;
    }
}


Forward.java

package tutorial.consts;

public class Forward {
    public static final String BY_MEHTOD_NAME = "sastruts.Forward.BY_MEHTOD_NAME";
}


行き過ぎたCoCはNGですが、強要しないし、マッピング系のCoCはありだと思っています。