04.基本概念:Controllerの処理フロー

概要

ここでは、Spring MVCでの最重要なControllerの動作について見ていきます。

具体的・実践的なサンプルは次の記事で触れますので、ここでは処理の概要を把握することが目的です。

まずControllerの骨組みだけで作ったソースコードを提示して、それから動作を1つ1つ見ていきます。

バインドも重要な内容ですので、前回の記事をまずお読みいただき、ご理解の上、読んでいただけると助かります。

【この記事で把握したいこと】

この記事で書きたかったのは、以下のことです。

・InitBinder(バインダーの初期化)が呼ばれるタイミング

・標準的なControllerでの処理とその順番

【先だって覚えておいてほしいこと】

Modelオブジェクト: Springが用意するMapオブジェクトで、Viewに渡すオブジェクトを設定します。

@Xxxx : アノテーションを表します。指定したメソッドをSpringが呼び出します。

Controllerクラスの骨組み

【Controllerクラスのサンプル】

//変更したユーザ情報の入力を受け取って、DBの値を変更する画面のコントローラです。

@Controller

public class SampleController {

@Autowired

private AccountService accountService;

@InitBinder("user")

public void initBinder(WebDataBinder binder) {

binder.setAllowedFields("id", "name");

}

//(1)

@ModelAttribute("user")

public User newRequest(

@RequestParam(required=false, value="id") String id,

) {

if(id== null) return null;

return this.accountService.get(id);

}

//(2)

@RequestMapping(value="/accountEdit", method=RequestMethod.POST)

public String form(@Valid @ModelAttribute("user") User user, BindingResult result) {

if(result.hasErrors()){

return "account-edit-input";

}

this.accountService.updateUser(user);

return "account-edit-complete";

}

}

【Userクラス】

public class User {

private String id;

private String name;

private Integer feeId;

//getter/setterは省略します。

}

後で少し詳しくSpringMVCにおけるController処理を見ていきます。

細かな内容はともかく、以下のことだけ覚えておいていただければと思います。

【通常のControllerの処理の流れ】

(1).@ModelAttributeの呼び出し : 事前準備として、DBからPOJOを取得して、Model に登録します。

(2).@RequestMappingの呼び出し: Model からPOJOを取り出して、画面処理をします。

【Controller処理のイメージ図】

※@InitBinder は、引数にモデルが存在するときに、モデルにバインドするたびに呼ばれます。

つまり、引数のモデルの数だけ呼ばれます。

以下で、上記の(1)、(2)をそれぞれもう少し細かく説明していきます。

Controllerの処理の流れ

(1)@ModelAttributeメソッドの処理の流れ

【再掲:実際のコード】

@ModelAttribute("user") //②モデルの前処理

public User newRequest(

@RequestParam(required=false, value="id") String id,

) {

if(id== null) return null;

return this.accountService.get(id);

}

【図の説明】

①Springでの処理

通常、initBinderメソッドを呼び出して初期化し、リクエストパラメタ("id")を、@ModelAttributeのメソッドの引数(id)にバインドします。

このサンプルでは、@InitBinder("id")が付けられたメソッドがないので、

initBinderのメソッドは何も呼ばれず、デフォルトのBinderでバインドします。

それにより、型変換された値が引数に渡されます。

②モデルの前処理

リクエストパラメタのうちUserオブジェクトのキーになるidを受け取り、DBから指定のidのオブジェクトを取得します。

③Springでの処理

@ModelAttributeのメソッドの返り値をModelオブジェクトにaddします。

【補足】

ModelAttributeメソッドは、リクエストのたびに、RequestMappingメソッドの前に呼ばれます

ModelAttributeメソッドは無くても動作する任意のメソッドです。

しかし使用した方がController処理がすっきりし、DB処理も簡単に記述できるようになります。

以下の(2)で見ることになりますが、たいていの場合、DBの更新をするときは、DBから取得したモデル(POJO)にこの画面で更新したいフィールドのみを

上書きし、Daoに渡すことになるからです。

(ModelAttributeメソッドでDBからPOJOを取得し、RequestMappingメソッドを呼び出すときにSpringが自動的に画面の値で必要なPOJOフィールドを更新する)

(2)@RequestMappingメソッドの処理の流れ

【再掲:実際のコード】

@InitBinder("user")

public void initBinder(WebDataBinder binder) {

binder.setAllowedFields("id", "name");

}

@RequestMapping(value="/accountEdit", method=RequestMethod.POST) //②コントロール処理

public String form(@Valid @ModelAttribute("user") User user, BindingResult result) {

if(result.hasErrors()){

return "account-edit-input";

}

this.accountService.updateUser(user);

return "account-edit-complete";

}

【図の説明】

①Springでの処理

@ModelAttributeで指定された"user"がModelから取り出されます。

もしModelに"user"がない場合、新たにUserをnewして、Modelに設定します。

今回のサンプルの場合、(1)のモデル前処理で既にModel に"user"が設定されているので、(1)のオブジェクトが取り出されます。

また、このサンプルでは、ModelAttribute("user")に対応する@InitBinder("user")があるので、

それが付けられたメソッドが呼ばれ、Binderが初期化されます。

取り出したUserオブジェクトに、Binderを介してリクエストパラメタを設定します。nameが"次郎"で上書きされ、user.feeIdは無視されます

initBinderメソッドで、パラメタ "user.feeId" を許可していないからです(DBのfeeIdの値を変更したくないので無視するように設定しました)。

最後にUserオブジェクトは②のメソッドの引数に引き渡されます。

②コントロール処理

自作したコントロール処理をします。

引数で渡ってきたuserは、Model 内のuserとインスタンスが同じため、userを操作するとModel 内のuserの値も変更されます。

ここではreturnとして、View名をStringで返しています。(実際にはreturnで返却できるクラスはString(View名)だけでなく、他にもあります。)

③Springでの処理

Modelと、②のメソッドが返したView名をまとめて、ModelAndViewを作成して、次の処理に渡します。

まとめ

ちょっとこまかかったでしょうか?

すみません。

把握してほしかったのは、InitBinderが呼ばれるタイミング、スタンダードなControllerでの処理とその順番です。

大まかに理解できていれば嬉しいです。

結局、DBからデータを取得し、リクエストの内容でDBを更新する、という流れです。

Springの場合、この一連の流れが、きれいに1つずつメソッドになっているので、分かりやすく、処理も簡潔に書けます。

付け加えておくと、(1)のモデル前処理は必須ではありませんので、必要に応じて使用することになります。

インターネットを検索するとSpring MVCのサンプルは数多く出てきます。

しかし、たいていのサンプルでは(1)の処理について省略しています。が、実践では必須になってくると思います。

なぜなら、リクエストパラメタ全てをそのままバインドすると、セキュリティホールになってしまうからです。

Created Date: 2012/04/07