😊

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
に来た場合:

  1. @ModelAttribute("pageableForm")
    が付いた初期化メソッドをSpringが実行
    setupPageableForm() が呼ばれ、Modelにオブジェクト登録
    → 中身は new PageableForm<>(new P0003Form())
  2. SpringがHTTPリクエストのパラメータをバインド
    page=2, conditionForm.productCode=A1000 を該当フィールドに設定
  3. コントローラの 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スキル開発など、元気と技術力を武器にお客様に真摯に向き合う価値創造企業です。
https://onewedge.co.jp

Discussion