Spring MVCにおける @ModelAttribute の2つの意味
Spring MVCにおける @ModelAttribute の2つの意味
Spring MVCでは、@ModelAttribute
というアノテーションが1つに見えて、実際には2つの異なる役割を持ちます。
特にフォームバインディングを扱う際に混乱しやすいため、この記事では「メソッドに付ける場合」と「引数に付ける場合」の違いを整理します。
1. @ModelAttribute の2つの使い方
用途 付与場所 呼ばれるタイミング 目的
① メソッドに付ける メソッド宣言に付与 コントローラメソッド呼び出し前 モデルに初期オブジェクトを登録する(初期値設定)
② コントローラの引数に付与 リクエスト→オブジェクトへのバインド処理に利用 リクエストパラメータをフォームオブジェクトにマッピングする
メソッド引数に付ける
2. メソッドに付ける @ModelAttribute
@ModelAttribute("pageableForm")
public PageableForm<P0003Form> setupPageableForm() {
PageableForm<P0003Form> form = new PageableForm<>();
form.setConditionForm(new P0003Form());
return form;
}
- 役割:モデルに「初期オブジェクト」を追加します。
- 呼ばれるタイミング:他のハンドラメソッド(@GetMappingなど)より先に実行。
-
効果:Modelに
"pageableForm"
という名前でオブジェクトが登録され、th:object="${pageableForm}"
で参照可能になります。
3. 引数に付ける @ModelAttribute
@GetMapping(PathConst.P0003._01.URL_PATH)
public ModelAndView list(ModelAndView mv,
@ModelAttribute("pageableForm") PageableForm<P0003Form> pageableForm)
-
役割:HTTPリクエストのパラメータを既存の
pageableFormへバインドします。 -
呼ばれるタイミング:
setupPageableForm()のあと。 - 効果:リクエストパラメータがオブジェクトのフィールドに自動的に反映されます。
4. 処理の流れ(内部的な順番)
リクエストが /viewer/list?page=2&conditionForm.productCode=A1000
に来た場合:
-
@ModelAttribute("pageableForm")
が付いた初期化メソッドをSpringが実行
→setupPageableForm()が呼ばれ、Modelにオブジェクト登録
→ 中身はnew PageableForm<>(new P0003Form()) - SpringがHTTPリクエストのパラメータをバインド
→page=2,conditionForm.productCode=A1000を該当フィールドに設定 - コントローラの
list()メソッド呼び出し
→ 引数pageableFormには既にバインド済みの状態で渡される
5. 図で理解する
HTTP GET /viewer/list?page=2&conditionForm.productCode=A1000
│
▼
┌────────────────────────────┐
│ @ModelAttribute メソッド │
│ setupPageableForm() │
│ → new PageableForm<>(new P0003Form()) │
└────────────┬──────────────┘
│
▼
Springが自動バインド
page=2
conditionForm.productCode=A1000
│
▼
┌────────────────────────────┐
│ @GetMapping list() │
│ 引数 pageableForm は既に反映済み │
└────────────────────────────┘
6. 実際の関係性まとめ
項目 メソッドに付けた @ModelAttribute 引数に付けた @ModelAttribute
タイミング Handler呼出し前 Handler呼出し時
動作 モデルにオブジェクトを「登録」 既存オブジェクトに「バインド」
戻り値 Modelへ追加 メソッド引数として受け取る
同名の場合 同じインスタンスが共有される 同じインスタンスを参照して更新
7. なぜ初期化メソッドが必要なのか
今回のように、PageableForm<T extends FormModel> の conditionForm
がジェネリックで、 Springが自動的にインスタンス化できない場合、
初期化メソッド (setupPageableForm) がないと500エラーになります。
ですので、setupPageableForm() は「フォームの雛形を登録」する役割、
list()
の引数は「リクエストで送られてきた値を流し込んだ実体」を受け取る役割です。
8. まとめ
観点 メソッドに付与 引数に付与
呼ばれるタイミング コントローラ実行前 コントローラ実行時
目的 初期オブジェクトをModelに追加 リクエスト値をオブジェクトへ反映
同名の場合 同じインスタンスが共有される 上書きされる
今回の例 setupPageableForm()で生成 → 理想的な流れ
list()で受け取る
結論:
> setupPageableForm() は「フォームの雛形を登録」し、
> list() の引数は「その雛形にリクエスト値を流し込んだ実体」を扱う。
この仕組みを理解しておくと、Spring MVC
のフォームバインディングは非常に明快になります。
株式会社ONE WEDGE
【Serverlessで世の中をもっと楽しく】 ONE WEDGEはServerlessシステム開発を中核技術としてWeb系システム開発、AWS/GCPを利用した業務システム・サービス開発、PWAを用いたモバイル開発、Alexaスキル開発など、元気と技術力を武器にお客様に真摯に向き合う価値創造企業です。
Discussion