JavaのWebアプリケーション開発フレームワークによる、Webサイト開発の顛末記です。

EclipseのMavenを使った、Spring-MVC、Thymeleaf、MyBatis 等のプログラミングテクニックを、
備忘録的に記録しています。実際に動くソースコードを多用して説明していますので、
これからEclipseや、Spring-MVCを始めたいと思っている人にとって、少しでも参考になれば幸いです。
Spring-MVCの散歩道 > SpringMVC の小径 > 第7歩 Spring-MVC 迷いの一歩 > データモデルバインド

■SpringMVC の小径 第7歩 Spring-MVC 迷いの一歩
7-1)データモデルバインド
桜の花もちらほら散りだして、そろそろ出発の時間がやってきました。
目的地はもう少し先です。名残おしい気分も分かりますが、
思い出を胸に刻んで、次のステップに踏み出しましょう。

前の節で、@RequestParamによる、viewからのリクエストパラメータを取得する方法と、
Mode へのレスポンスパラメータの追加で、viewにレスポンスする方法は習得できました。
ただ、これを実際の業務のプロジェクトに当てはめて考えてみたらどうなるでしょう。
リクエストパラメータが1個か2個ぐらいだったら何とか我慢できます。
しかし、実際の業務では少なくても一画面に10個ぐらい。
多い時は一画面あたり数十個ものリクエストパラメータが来ることがあります。
コントローラメソッドはこれを受けるために、
@RequestParam("param_1") ~ @RequestParam("param_X")までメソッド引数が並ぶことになります。
レスポンスもしかり、
model.addAttribute("param_1", "aaaa");
    ~ 
model.addAttribute("param_X", "zzzz");
まで長蛇の列が並ぶことになります。
こんなコントローラが、数十画面から数百画面分あったらどうします?
「きっと嫌になる!」と、ここだけは絶対的確信を持って言えます。(笑)

んじゃ、どうすんのさ?
当然そんなイバラの道を好んで歩きたくはないので、安全な逃げ道を探しましょう。
前の節の後半部で、コントローラメソッドの引数には様々指定できる話をしました。
その中で、@ModelAttribute というアノテーションがありましたが、覚えてますよね?

んーと。。。。。。。。。おいらは「すぷりんぐ太郎」だじょ?

まぁいいでしょう。ふぅ。
待っててあげるので、もっかい回れ右して見直してきてください。

はぃ。ありましたね。
この@ModelAttributeを使います。
で、具体的な使い方についてですが、言葉で説明するのがちょいと複雑で難しいので、
実際動くプログラムで説明してしまいます。
※)その前に、一つ二つ注意事項。
  @ModelAttributeの使い方は、紹介されているサイトによっては
  これから解説する方法とは微妙に違うやり方で実装例が紹介されているケースもありますが、
  ここでは、パラメータクラス(FormBeanと私は呼んでいます)を、Modelにバインドする
  という、少しだけ手間をかける方法を採用しています。
  理由はいろいろありますが、ここでは先を急ぐので割愛。
  なので、ここと異なる使い方があってもそれは誤った説明ではなく、それも正解です。
  Spring-MVCの散歩道は、いくつにも枝分かれしている迷路なのです。


さて、この節ではHelloはもう過去の思い出です。過去は、思い出の中に刻み込んで、
新しい、viewとコントローラ、それに今回は、パラメータクラス(FormBean)と、そのインターフェース
を全部新しく作ることにしましょう。
内容としては、Helloに似ていますが、入力パラメータを4つに増やして、コントローラで一気に受け取るサンプルです。
まずは view 側 入力用HTML(WebContent/promenade.html)

基本的には、Hellで使ったものと同じですが、入力項目が4種類に増えています。
次は、パラメータFormBean(src/main/java/jp/dip/arimodoki/model/PromenadeForm.java)

promenade.htmlからsubmitされたリクエストパラメータの内容を格納するFormBeanクラスです。
このクラスは、@Componentアノテーションを宣言しています。
クラスレベルに指定できるアノテーションには、
@Controllerアノテーション
@Componentアノテーション
@Serviceアノテーション
@Repositoryアノテーション
と、もう一つリクエストパラメータをJSONで扱う場合に便利な、@RestControllerアノテーション
があります。 どのタイプのクラスに、どのアノテーションを使用するか厳密な規定は無いようですが、
個人的にはコントローラクラスには、@Controller、@RestController
ビジネスロジック(サービスロジック)クラスには、@Service
FormBeanのようなパラメータ格納クラスには@Component
DBアクセス層のクラスには、@Repositoryを使用する様にしています。
@Componentアノテーションを宣言することで、
コントローラクラスから、このすぐ後に出てくる、@Autowiredアノテーションを使って
クラスのインターフェースを介してこのクラスをDIします。
ここで注意することは、これらのアノテーションを宣言していないと
クラスをDIしようとしても、Spring-MVCは無視してしまうので注意してください。
もしこの状態で、不用意にプログラムを実行すると、NullPointerExceptionが発生します。
※)accessorメソッド(getXXX, setXXX)は、Eclipseで自動出力しているのでコメントなどが入っていません。

FormBeanインターフェース(src/main/java/jp/dip/arimodoki/model/PromenadeFormIf.java)

PromenadeFormのインターフェースです。
コントローラ側で、PromenadeFormクラスをDIする際に使用されます。
※)ちなみにこれも、Eclipseで自動出力しており、自分で書いたりしていません。

そして、コントローラ(src/main/java/jp/dip/arimodoki/cntl/Promenade.java)

ここで、腱鞘炎が少し辛くなってきたのでちょっと手抜きして、
ソースのコメントに直接書き込んだ解説を読んでください。^^);
こちらの方が直感的でわかりやすいかも?
一つだけ大事な説明を忘れてました。
コントローラクラス変数 PromenadeFormIf の上に、
@Autowired という新しいアノテーションが出現しています。
なにかというと、これこそがまさにSpringフレームワークの神髄(微妙に違うかもしれませんが)
「Dependency Injection:依存性の注入」です。
クラス変数(インターフェース)に、@Autowiredアノテーションを付けてあげることで、
このクラスがロードされるときに、この変数も自動的にDIでインスタンス化されます。
やってることは、PromenadeFormIf promenadeForm = new PromenadeForm();
と違いはないのですが、DIのメリットを説明するのが難しいので(手首が痛くて手抜きしたいので)
まぁ、そういうものだと思ってください。(責任放棄)

ソースにごちゃごちゃとコメントを入れたので、ちょっと読みにくいですかね?
コメントと、余分なデバッグ文をはずすとどうなるでしょうか?

@Controller
public class Promenade {
  @Autowired
  private PromenadeFormIf PromenadeForm;

  @ModelAttribute("PromenadeFormData")
  public PromenadeFormIf setupBind() {
    return this.PromenadeForm;
  }

  @RequestMapping(value = "/promenaderes")
  public String promenade(
    @ModelAttribute("PromenadeFormData") PromenadeFormIf formbean
    ) {
    return "promenaderes";
  }
}

オーマイガッ!なんてこったい。こんなにやつれ果ててしまって!
お~ぃ。君~ぃ! 生きてるかぁ~!!!!!?

最後に view 側 入力結果表示用HTML(WebContent/promenaderes.html)

こちらも同じく、結果表示用のプロパティが4種類に増えています。
Thymeleafの新しいタグが一個増えています。th:object="${PromenadeFormData}"
これは、コントローラでFormBeanを、”PromenadeFormData”という名前で、Modelにバインドしたので、
view側では、"PromenadeFormData"という名前で、FormBeanのオブジェクトを取り出しています。
このオブジェクトが持っているプロパティ myname などを、
th:text="${PromenadeFormData.myname}"というやり方でアクセスできますが、
数が多いと、いちいちPromenadeFormDataを書くのが面倒なので、
th:object="${PromenadeFormData}"でオブジェクトを取り出して、
*{変数名}のようにワイルドカードが使用できます。(すんばらしい!)

 

では、さっきの骨皮筋衛門さんが、ほんとに生きてるのか確認してみましょう。
前と同じく、Eclise「サーバービュー」でローカルサーバーを起動し、
ブラウザのURLに「http://localhost:8080/j_Labo/promenade.html」を入力します。
花子とはもうおさらばさ
四カ所の入力項目に入力したら、「回答しちゃるけん」ボタンをクリックします。
おいらはすぷりんぐ太郎だじょ
どうでしょう。
先ほどのやせ細った体のコントローラでも、立派に4つのパラメータを処理し、結果画面に出力されましたね。
ただし、この処理には一つ問題点があります。
年齢の入力項目に、数字以外のアルファベットなどを入力すると、400エラーが発生します。
このサンプルでは、例外処理はまだ一切入れていないので、そこはご愛嬌ということでお願いします。
例外処理については、次の節で解説する予定です。
Hello の時と同じく、リクエストパスのxhtmを htmlに変えれば素のhtmlが表示できるのは同じです。
いかがでしたか?
@ModelAttributeを使えば、FormBeanクラスは作らなければなりませんが、
前節で作った、@RequestParamを使用する方法より、よりすっきりしたコントローラになりました。

ものすご~くシンプルなサンプルですが、
これに、もうちょっと例外処理をとかを付け足してあげたりすれば、
このサンプルは、実際の業務でも立派に使えるコントローラの基本となるモジュールです。

ただ実際は業務フローに基づき(?ないがしろにされて?)、
エンドユーザーのわがままとか、しがらみとか、政治的な圧力とかの複雑な理由により、
こんなに美しかった基本形のソースは、次第に泥にまみれて汚されていく運命を辿ることになるのです。