✅ はじめに
前回の Vol.11.4 では、Struts2の自動バインディングの仕組みと、ParametersInterceptor
の役割について解説しました。
本記事では、実際のフォーム入力と Action クラスのプロパティ間でどのようにデータがバインドされるのかを、具体的なパターンごとに図解的に整理して解説します。
🗂️ 目次
- 単純なプロパティのバインディング
- 配列とListのバインディング
- Map形式のバインディング
- チェックボックス・複数選択のケース
- バインディング失敗・型変換エラー時の挙動
- オブジェクト型プロパティのネスト
- 補足:バインディング対象を制御するには?
- まとめと次回予告
1. 単純なプロパティのバインディング
もっとも基本的な構成。フォームから送信された入力値が、Actionクラスのプロパティにそのままマッピングされます。
HTMLフォーム:
<input type="text" name="userId" />
Actionクラス:
private String userId;
public void setUserId(String userId) {
this.userId = userId;
}
2. 配列とListのバインディング
フォーム名に [インデックス]
を指定すれば、配列やListにもバインドできます。
HTMLフォーム:
<input type="text" name="userIds[0]" />
<input type="text" name="userIds[1]" />
Actionクラス:
private List<String> userIds = new ArrayList<>();
public void setUserIds(List<String> userIds) {
this.userIds = userIds;
}
📝補足:
JSで動的にフォームを生成する場合、userIds[] のように記述し、送信前にインデックスを付ける手法も一般的です。
"You can loop them by name or something, and change their name with JavaScript by assigning the right index."
— Stack Overflow
3. Map形式のバインディング
フォーム名に map['key']
形式を使うことで、Mapにもバインド可能です。
HTMLフォーム:
<input type="text" name="params['key1']" />
<input type="text" name="params['key2']" />
Actionクラス:
private Map<String, String> params = new HashMap<>();
public void setParams(Map<String, String> params) {
this.params = params;
}
4. チェックボックス・複数選択のケース
チェックボックスや複数選択のselect
は、複数の値が一括でバインドされます。
HTMLフォーム:
<input type="checkbox" name="selectedIds" value="1" />
<input type="checkbox" name="selectedIds" value="2" />
Actionクラス:
private String[] selectedIds;
public void setSelectedIds(String[] selectedIds) {
this.selectedIds = selectedIds;
}
5. バインディング失敗・型変換エラー時の挙動
例:数値型のフィールドに文字列が送信されると、変換エラーになります。
HTMLフォーム:
<input type="text" name="age" value="twenty" />
Actionクラス:
private int age;
public void setAge(int age) {
this.age = age;
}
この場合 age
には正しく値がバインドされず、型変換エラーが発生します。Struts2は変換エラーを検知し、適切にエラーメッセージを生成します。
6. オブジェクト型プロパティのネスト
Struts2では、リクエストパラメータのキー名に「.(ドット)」を使うことで、ネスト構造のモデルオブジェクトにも自動で値がセットされます。
🧩 ケース概要
例えば、User
モデルの中に name
プロパティを持つ場合、以下のようなリクエストパラメータが送られることで:
user.name=Taro
Struts2は ParametersInterceptor を通じて以下のような連鎖的バインディング処理を行います:
🔁 自動バインディングの流れ
-
"user.name"
というキーを検出 - アクションの
getUser()
を呼び出し、戻り値としてUser
オブジェクトを取得 - その
User
オブジェクトに対して、setName("Taro")
を実行
user.name
↓
getUser()
↓
Userオブジェクト
↓
setName("Taro")
このように、「ネスト構造のオブジェクトにも値をセットできる」 のがStruts2のOGNL×ParametersInterceptorの強みです。
💡 実装例
🔹 アクションクラス(Action)
public class RegisterAction extends ActionSupport {
private User user = new User();
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String execute() {
// user.getName() や user.getEmail() などを使って処理
return SUCCESS;
}
}
🔹 モデルクラス(User.java)
public class User {
private String name;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
🔹 HTMLフォーム(JSPなど)
<s:form action="register">
<s:textfield name="user.name" label="名前"/>
<s:textfield name="user.email" label="メールアドレス"/>
<s:submit value="登録"/>
</s:form>
🧠 補足:get/set 両方定義する理由
実は、技術的には getUser()
だけでも内部的に User
インスタンスが取得できるため、バインディングは可能です。
ただし、以下の理由から setUser()
も合わせて定義するのが実務では一般的 です。
-
DIやユニットテストの都合(モック注入や差し替えが可能に)
-
保守性の向上(将来的に
User
の生成方法を差し替える可能性) -
プロジェクトの設計統一(全フィールドに対して
get/set
ペアを定義)
✅ ポイントまとめ
パターン | バインディング方式 | 備考 |
---|---|---|
user.name=Taro |
getUser() → setName()
|
OGNL によるネスト解決 |
getUser() のみ定義 |
動作するが保守性にやや難あり | 推奨されない設計 |
getUser() + setUser() 両方定義 |
◎ベストプラクティス | 実務ではこの形が主流 |
🧭 OGNLとParametersInterceptorの連携によって、Struts2はJavaBeans風オブジェクトのプロパティに対しても「パス指定で値を注入する」ことができます。このパターンを理解すれば、複雑なフォームでも柔軟に対応できます!
7. 補足:バインディング対象を制御するには?
Struts2では以下のような制御が可能です。
-
excludeParams
:特定のパラメータを除外 -
includeParams
:許可するパラメータを明示 -
paramsPrepareParamsStack
:Prepare処理 → パラメータ設定 の順に制御できるStack
"This interceptor gets all parameters from ActionContext and sets them on the ValueStack via OGNL."
— Apache Struts: Parameters Interceptor
8. まとめと次回予告
本記事では、Struts2における 自動バインディングの実際の動作 を、以下のようなパターン別に解説しました:
-
単純なプロパティ
-
オブジェクトネスト
-
配列・リスト
-
Map形式
-
チェックボックス・セレクト
-
型変換エラーの挙動
👉 次回予告
Vol.11.5:struts.xml完全理解ガイド(仮)
設定ファイルの構成・設計・ベストプラクティスを深掘りしていきます。
🧭 関連記事(Vol.11.xxxシリーズ)
連番 | タイトル | 内容分類 |
---|---|---|
Vol.11.0 | Struts2が提供してくれる便利機能まとめ9選 | 導入&概要 |
Vol.11.1 | 自動注入とステートレスActionの仕組み | フレームワーク内部理解 |
Vol.11.2 | UX改善に効く!アクション層設計と入力制御の極意(プロローグ) | UX設計・アクション層総論 |
Vol.11.2.1 | 【実践編】Struts2アクション層の役割と設計パターン(Command型 / UIアクション分離など) | アクション設計パターン |
Vol.11.2.2 | 【UX重視】戻るボタン・キャンセル処理の設計手法と「戻り先管理」のスマート実装 | 戻り先制御とUX設計 |
Vol.11.2.3 | 【設計ノウハウ】多画面遷移時のパラメータ受け渡し・保持戦略(セッション vs hidden vs URL) | パラメータ多重管理 |
Vol.11.2.4 | 【実践ガイド】ユーザー操作を考慮したアクション遷移設計(リダイレクト vs フォワードの使い分け) | UX遷移設計/Result制御 |
Vol.11.2.5 | 【トラブル防止】アクション層の共通処理とメンテナブルなBaseAction設計 | アクション共通化/保守性 |
Vol.11.3 | Struts2の defaultStack とは?Interceptorの中身を詳しく追ってみる | Interceptor |
Vol.11.3.2 | 🔧 Vol.11.3.2 独自Interceptorの作り方と使い所 Struts2のカスタマイズを“実践で使えるレベル”に一歩進めよう | Interceptor |
Vol.11.4 | 入門者のための「パラメータ自動バインディング」完全理解ガイド | 自動バインディング |
Vol.11.5 | Struts2の構成理解が“実務レベル”へと進化する決定版 | 設定ファイル構造 |
Vol.11.5.1 | Struts2の“画面遷移の全体像”を理解するためのマイルストーン記事 | 設定ファイル構造 |
Vol.11.6 | ActionSupport の便利メソッドと国際化対応 | ユーティリティ / i18n |
Vol.11.7 | validate() / input 戦略とUX設計 | 開発スタイルの違い |
Vol.11.8 | アノテーションベースの設定 vs XML定義の比較 | 開発スタイルの違い |
Vol.11.9 | バリデーション完全解説(XML / アノテーション) | バリデーション(※Vol.8補完) |
Vol.11.10 | Struts2の「OGNL」ってなに?しくみと使い方をやさしく解説 | 式言語OGNL解説 |
Vol.11.11 | ModelDriven インターフェースの使いどころと注意点 | Model駆動開発 |
Vol.11.12 | SessionAware と RequestAware でセッション/リクエスト管理 | セッション管理 |
Vol.11.13 | Resultタイプ完全解説(dispatcher / redirect / stream など) | 遷移パターン理解 |
Vol.11.14 | Vol.11.14 ValueStackの中身を理解する – 画面とActionの橋渡し役 | Action |
Vol.11.15 | Interceptorチェーンを自作してみよう(ログ / 認証フィルタ) | Interceptor |
Vol.11.16 | エラー処理と例外ハンドリングの実践パターン | ハンドリング設計 |
Vol.11.17 | Struts2でファイルアップロード機能を実装する方法 | 実装系Tips |
Vol.11.18 | 非同期通信(AJAX)とStruts2の連携方法 | フロント連携編 |
Vol.11.19 | セキュリティ対策(CSRF, XSS, パラメータ偽装) | セキュリティ対策 |
Vol.11.20 | 複数フォームを安全に扱う!セッション管理と動的フォームの考え方 | セッション管理 |
Vol.11.21 | Struts2でJSONを返す!REST API連携と非同期通信の基本設計 | セッション管理 |
Vol.11.22 | 本番環境構成とセキュリティ設計(Apache+Tomcat+Struts2連携) | 外部公開 |