Spring MVCのメモ
Spring MVCをmamatumoさん風にメモしてみるテスツ。ただし、千葉が思い出しながら書いてるから嘘の可能性もあり。
Spring MVCの概要
- ベースはServletとJSPの世界
- ただし、ServletはDispacherServletだけだったり
- JSP以外のビューも視野に入れていたりはする。
- 即ち、FORMタグのACTION要素に指定したURLをweb.xmlでDispacherServletにマッピングする。
- 言う必要も無いだろうけどweb.xmlではパターン表記でマッピング
- なんでかは知らんがACTION要素にはxxx.htmlやxxx.formが指定されることが一般的ぽ。
- 当然、xxx.htmlなんて実際には作る必要は無い。
- DispacherServletは<DispacherServlet名>-servlet.xmlという名前で用意したSpringのアプリケーションコンテキストを見て、URLパターンに適したコントローラクラスに処理を振り分ける。
- ビジネスロジック用クラスはアプリケーションコンテキスト内でコントローラにインジェクションしてコントローラから呼び出す。
- バリデーション用クラスはアプリケーションコンテキスト内で定義し、コントローラの主処理が呼び出される前にDispacherServletが勝手に呼び出す。
- 実際はコントローラのバリデーションメソッドが呼び出されてて、そのバリデーションメソッドの中でバリデーション用クラスに処理を委譲してるんだとかなんだとかだったような気がしないでもないけど忘れた。
- 処理結果のビューへのフォワードはコントローラの戻り値とアプリケーションコンテキスト内の設定を元にDispacherServletがよしなにフォワードする。
- コントローラはインターフェースと、それを実装したものがいくつか容易されており、インターフェースを一から実装するか実装済みのものを目的にあわせて選択し、継承して実装する。
他のフレームワークとの違いとウリ
長所っぽいところ
- 入力データはJavaBeansの形でコントローラに渡される
- Spring MVC用語では「command」と言うらしい。
- バリデータクラスにも「command」の形で入力データが渡される。
- バリデータクラスは完全にSpring MVCから独立している。
- よって、バリデーションも含め、いわゆるビジネスロジック的なところは「command」さえ渡してやれば、プレゼンテーション層がWebじゃなくなっても動く。多分。
- 多分POJO的でテストしやすい。
- 仕組み上嫌でもDIコンテナの上で動くので、嫌でもDIコンテナの恩恵が普通に受けられる。
短所っぽいところ
- コントローラそのものはシグニチャにHttpServletRequestやHttpServletResponseを持っていたり、実用性を考えると何かしらの基底クラスを継承しなければならなかったりと、何かしらへの依存度が強そう。
- バリデータが完全に独立している→組み込みバリデータがちっともなさげ。確かに組み込みバリデータに頼るとWeb層から切り離した場合組み込まれてた分の検証処理は別途実装が必要になってアボンはするが、とはいえバリデーション全部いちいち実装も辛い。
- これはSpring MVCというよりSpringそのもの傾向だけど、やっぱりXMLファイルが肥大する傾向があると思う。
- とはいえ、faces-config.xmlやらstruts-config.xmlやらを書かなくていい分、JSFやStrutsと組み合わせるときよりは減るっちゃ減るかも?
- その分アプリケーションコンテキストに書く内容が増えるから種類は減っても量的にはどっこいどっこいかも?
- でも量が一緒なら種類が減るだけましか。
- 傾向的にはStrutsが持っていた短所と同じ傾向にあるような気がしないでもない。
- 生Strutsよりは明らかに短所は少ないだろうけど、Struts+Springと比べるとどーなんでしょね。
個人的に
- コントローラの依存度の強さと
- 下に書いた通り1ページ:複数コントローラ(1ページ:1コントローラでは無い)なのと
- 組み込みバリデータが無い
- のは気になるけど、
- バリデーション含めてビジネスロジックのWeb層に対する依存度の低さとDIコンテナとの親和性(当たり前か)を考えて意外といけるんじゃないかなーなんて思うのだが、
- 他のStruts 1.x後継を狙うフレームワークと比べて目に見えて真新しい/魅力的な/一般大衆受けするアピールポイントが見えない。
- JSF→Java EE5では標準になる
- wicket、Tapestry→JavaとHTMLだけで作れる(XMLやJSPいらず)
- Struts 2.0→Struts直系
- などなど
- よって後継争いはきっついだろうし広まるような気はしないけど俺的には嫌いじゃない。
- 本格的に試してみたい気もちょっとする。
Spring MVCアプリの構成要素
Servletベースのフレームワークで、開発者は下記の要素を作成する。
- web.xml
- アプリケーションコンテキスト
- DispacherServletごとに1ファイルと、共通用のものをもう1ファイル(必須ではないけどあった方が多分便利)。
- JSP
- 「Velocity や XSLT などを利用することも可能である」とかこの辺に書いてあるけどどうなんだろね。
- Javaオブジェクト
- 最低でも以下のもんは必要
- でもMVCを考えると、
- あたりも普通別クラスか。後はデザインパターンによってFacadeだったりなんだったり好きなように。
Versionの変遷
- 現在は1.2系になるのかしら。
- 2.0系からはコントローラ内で利用してたModelがHashMapからSpring独自のMapオブジェクトになったりなんやかやあるらしいけどなんやかやあるんだろう、きっと。
Spring MVCの画面遷移
- 基本的に、次の遷移先はコントローラの戻り値にセットし、それを元にDispacherServletが遷移させる。
- コントローラの戻り値は「ModelAndView」という独自の型で、Viewとして次の遷移先を、Modelとしてその遷移先に渡す値をセットする。
- Modelは1.2系ではHashMapで、キーとともにJavaBeansを値としてセットする。
- ViewにはJSP名をセットする。
- /■■■/XXX/△△△.jspというのが遷移先とすれば、△△△のみをコントローラ内でセット。
- /■■■/XXX/はprefix、.jspはsuffixとしてアプリケーションコンテキスト内で設定。
- したりする方法とか、prefixもsuffixも△△△もぜーんぶプロパティファイルに書いといてコントローラからはプロパティ名だけを指定する方法とか、コントローラ内にフルパスで指定したりとか、まー色々あるらしい。
- JSFのナビゲーションルールよりは設定ファイルに書くこと少ない気がする、多分。
Spring MVCのFormの扱い
ここにはmamatumoさんは何を書こうとしていたのだろう?
- とりあえず、Formを扱う用のコントローラとしてSimpleFormControllerというのがあって、普通はこれを継承する。
- なんや、StrutsのActionFormみたいなもんか?といわれるとそうかもしれないがStrutsには手を付けていないので実際のところ知らん。
- 1フォームに1コントローラつくイメージ。
- MultiActionControllerというのを使えば複数フォーム扱えるけど、SimpleFormControllerと違って入力データをJavaBeansとして渡してくれなかったり(HttpServletRequestから自分で取得する必要あり)、バリデーションクラスを自動で呼んでくれなかったりと一気に生のServlet色が濃くなるので、たくさん項目があるフォームに対してはあまり実用的でない。
- よって、JSFみたく1ページに1ManagedBeanというのは辛そう。
- やってやれないことはなさそうだけど、1ページにFORMごとの複数コントローラの方が楽そう。
- 1フォームの中に複数アクションを持たして1つのSimpleFormControllerに処理させるのは、JavaScriptとか使って作りこめばそんなに辛くなさそう。
- GET/POSTなどのメソッドは、アプリケーションコンテキスト上で受け付けるかはじくか設定可能(POSTだけ受け付ける、とか)。
- Sessionを持っていないリクエストをはじくのもアプリケーションコンテキスト上の設定で可能。
- 基本的にGETメソッドは初回アクセス(初期値表示)用として捕らえているような感じで、GETとPOSTでは同じコントローラでも処理フローが多少違う。
Spring MVCの入力検証
- 上の方で書いたとおり、
- Spring MVCから独立したバリデーションクラスを用い、
- コントローラの主処理の前にバリデーション実行
- エラーがある場合は、コントローラの主処理をすっとばして元ページのタグで指定した場所にエラーメッセージを表示
- するっていうのは、まぁそんなに特異な処理ではないと思う。単項目検証や入力項目間の整合性検証はこれでうまくいくけど、EIS層のデータとの間での整合性検証が問題。
- 思想的にバリデーションはバリデーション用クラスで完結し、ビジネスロジック用クラスに持ち込ませないような思想を見受けるので、
- バリデーション用クラスで単項目検証・入力項目間の整合性検証→コントローラ→ビジネスロジック用クラス→ビジネスロジックの一部として入力項目・EIS層のデータ間の整合性検証とやると、エラーを元の画面に返すのに何かしら工夫する必要がありそう。
- 流れ的には、バリデーションクラスからDAOを呼んで(Facadeとかを間に挟んでもいいけど)バリデーション用のデータを取得し、バリデーションクラス内で入力項目・EIS層のデータ間の整合性検証をやってバリデーションをここで完結させるのが自然ぽいけど、1リクエスト内でバリデーションロジックとビジネスロジック別々にデータベースアクセスするのもなんか美しくない。
- プーリング機能を使えば処理負荷はそうかからんだろうけども。
- そこでAOPを使って宣言的トランザクションですよと言われればそうかもしれないけど、
- こないだ1△13がhogeBSで宣言的トランザクションやったけど結局設計者(実装者も?)から何処から何処までが同一トランザクションか分かりづらいと言われたみたいなことゆってたので、とりあえず宣言的トランザクションからは離れて考えたい。
- ってかそもそもビジネスロジックはDispacherServlet→コントローラ→ビジネスロジック、バリデーションから直接DAOを呼び出す場合はDispacherServlet→バリデーションクラス→(ビジネスロジック用クラス→)DAOとなるので、これを同一トランザクションっつーことはそもそもはDispacherServletからトランザクション開始しなければならず、単項目検証でチェックエラーなど本来DBアクセスを必要としないケースでもトランザクションはっちゃうことになっちまうのでコネクションプーリングの無駄遣いも甚だしい。
- やっぱ別々にDBアクセスするのが一番ましっぽ
最終更新:2006年08月10日 21:33