SAStrutsとStrutsのアクションのスコープの違い
SAStrutsとStrutsはアクションのコンポーネントの
スコープが異なっているので注意しなければならない。
アクションのスコープの違い
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は、JavaとRubyの美味しいとこ取りしたような印象。
社内のRuby好きな人も高い評価をしていました。
SpringJDBCを使ってDBにアクセスするDAOをScalaで実装していたので、Seasarとの連携を試したくなりました。
詳解! PostgreSQL 3.3
パッと見ただけで、内容の濃さが伝わってくる。
性能向上に関するトピックを実測値を踏まえて紹介。
つくづく「速さは力なり」と思う。
よりスマートな画面遷移?
先日、社内のRailsな人にSAStrutsのことを軽く説明する機会がありました。
以前のエントリーに書いた『画面に表示するデータを準備する』メソッドの
名前は遷移先のjspファイル名にあわせる、という非公式の私のお作法を説明した後に、
以下のようなコードを見てもらったのですが、どうも気に入らないと。
@Execute(validator = false) public String hoge() { return "hoge.jsp"; }
理由は次のとおり。
うーん、なるほど。一理ある。
ということで、以下のような感じで記述できる
インターセプター(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はありだと思っています。