概要ここでは、Spring MVCの実際のサンプルを見てみます。
日本語のWEB上で紹介されているサンプルは、学習の導入用のものが多く、Hello World的な参照画面で終わっています。 ここではもう少し掘り下げたものを見てみたいと思います。 【環境など】
【ソース一式をダウンロードできるようにしました】
【より実践的なサンプル】
目標 まず、以下のサンプルの目標(ゴール)を示します。
【動作について】 入力画面 ⇒ 確認画面 ⇒ 完了画面、という画面を作成していきます。 画面とControllerの連携のパターンとしては、「複数の画面に1つのControllerを結びつけるパターン」を使用します。 (パターンについてはこちらの記事を参照) 【URLと処理メソッドのマッピングの実現方法】 URLと処理メソッドのマッピングの方法は、いくつか考えられます。 ここでは、Controllerクラスにアノテーションでマッピングを記述する方法をとります。 実際には、例えば「http://localhost:8080/sample/user/edit.html」というURLの場合、 親のパス「/user」までを設定ファイルで管理して、「edit」のマッピングだけをControllerに記述する設計もやりたいはずです。 親のパスまでControllerのプログラマーに管理させてしまうと、全体的なURLパスの構成を設計者が管理できないからです。 このような場合のサンプルはまた別の記事で見ていこうと思います。 【リクエストパラメタを受け取るモデルについて】 以前の記事でも書きましたが、業務モデルをそのまま、リクエストパラメタを受け取りに使用しない方がよいです。 このサンプルでもFormクラスを作成し、その中で業務モデルを使用する方法をとります。 このサンプルではクラスを増やすのが面倒でしたのでinnerクラスで実装しましたが、外部にした方が分かりやすいかもしれません。 このあたりは設計しだいかと思います。 【妥当性チェックの実装方法】 Springでデフォルトで用意されている妥当性チェックは、Spring Modules、Hibernate Validatorなどです。 いずれにしても、どのようなValidatorもカスタマイズすれば使用できるようになります。 ここでは標準的に使用されるHibernate Validatorを使用します。必要なjarファイルも用意してください。 【例外処理について】
今回は例外処理の設定はしません。 また別の記事で扱おうかと思っています。 コードの後に簡単な説明を入れますので、それを読みながら実行していただければと思います。
一番下にイメージ画像をつけましたので、先にそちらを見ていただいても良いかと思います。 使用サンプル<web.xml の記述サンプル>
<display-name>test</display-name> <welcome-file-list> <welcome-file>top.jsp</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- spring用のリスナー--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/applicationContext-webmvc.xml </param-value> </context-param> <!-- エンコード --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Spring MVCの設定 --> <servlet> <servlet-name>spring_mvc_sample</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value></param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>spring_mvc_sample</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> </web-app> SpringのDispatcherServletを設定します。 <画面処理サンプル(Controller): com.sample.controller.UserController.java>
import java.text.SimpleDateFormat; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import com.sample.business.model.User; import com.sample.business.service.UserService; /** * このアノテーションをつけて、component-scanさせるとControllerとして扱われます。 */ @Controller @RequestMapping(value="/user/") public class UserController { @Autowired private UserService userService; /** * formモデルのバインダーの初期化。リクエストパラメタをモデルに変換するたびに呼ばれる。 */ @InitBinder("form") public void initBinderForm(WebDataBinder binder) { //バインドするときの日付のフォーマット指定。 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, "user.upDate", new CustomDateEditor(dateFormat, true)); //Userオブジェクトのうち、user.nameパラメタを受け取りたくない場合 binder.setAllowedFields("user.age", "user.upDate"); } /** * モデルオブジェクトの初期化 * (サンプルのためセキュリティの考慮がないことに注意! * ログイン時のユーザIDと以下のuserIdが一致するかをチェックし、一致しない場合エラーにすべき。) */ @ModelAttribute("form") public Form newRequest( @RequestParam(required=false, value="user.id") String userId ) { Form f = new Form(); // User user = null; if(userId == null){ user = new User(); }else{ user = this.userService.getUser(userId); } // f.setUser(user); return f; } //リクエスト処理------------------------------------------ @RequestMapping(value="edit/input", method=RequestMethod.GET) public String input(Form form) { //既にnewRequestでモデルをDBから取り出し、設定しているので何もする必要がない return "user-Edit-Input"; } @RequestMapping(value="edit/confirm", method=RequestMethod.POST) public String confirm(@Valid Form form, BindingResult result) { //@Valid を指定したモデルは妥当性チェックが実行される。 if(result.hasErrors()){ return "user-Edit-Input"; } return "user-Edit-Confirm"; } @RequestMapping(value="edit/finish", method=RequestMethod.POST) public String finish(@Valid Form form, BindingResult result) throws Exception { if(result.hasErrors()){ return "user-Edit-Input"; } //データ更新 this.userService.updateUser(form.user); return "user-Edit-Finish"; } //--------------------------------------------- //フォーム(HTML用のパラメタを受け取れるように作っておいた方がよいと思います) public static class Form{ @Valid private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } } } 詳細は後の記事で触れます。ここでは、簡単な説明にとどめます。 【@Controllerについて】 このアノテーションをつけたクラスをControllerとして扱います。 ただし、Spring設定ファイルにこのアノテーションを有効にする記述(mvc:annotation-driven、context:component-scan)を しなければなりません。 【@InitBinderについて】 Binderの初期化をすることができます。 上記では、upDateプロパティに日付のフォーマットを指定しています。 この日付の設定はJSPでも適用されるので、HTML上でも、"2012/04/07"のように出力されることになります。 また、setAllowedFields()メソッドで、設定を許可するプロパティを指定できます。逆に許可しないプロパティを設定することもできます。 【@ModelAttributeについて】 このアノテーションは、メソッドにつけることも、引数につけることもできます。 メソッドに付けた場合は、前の記事で見たモデル前処理になります。 引数に付けた場合は、Modelオブジェクトから指定の名称のオブジェクトを取得して、引数に渡すようになります。 また引数の場合、このアノテーションは省略でき、上記のinput()、comfirm()、finish()メソッドでは省略されています。 省略時の名称は、クラス名の先頭を小文字にしたものになります。(例:@ModelAttribute("form")) 【@RequestMappingについて】 このアノテーションでURLとのマッピングをしますが、クラスにつけることも、メソッドにつけることもできます。 クラスにつけると、URLの親のパスにマッチし、さらにメソッドに付けたパスが、親からの相対パスになります。 ですので、例えばinput()メソッドは、「user/edit/input.html」のパスのリクエストについて呼び出されることになります。 【画面処理メソッドの引数について】 StrutsなどのWEBフレームワークでは、引数が決まっています。しかしSpring MVCでは自由に引数を決められます。 例えば、HttpServletRequestを指定すればそれを渡してくれます。引数の順番もルールは特にありません。 例外として、BindingResultだけは@Validをつけたモデル引数の後に記述しなければなりません。 参考: 処理メソッドの引数と返り値 【@Validについて】 これをつけた引数は、妥当性チェックを行います。 以前の記事で見ましたように、Controllerのメソッドでは引数を渡すときに、initBinder()メソッドを呼び出してBinderを初期化します。 そのBinderでバインドしたオブジェクトを渡します。このときに、@Validをつけた場合だけ妥当性チェックを行います。 参考: 10.妥当性チェックについて 【Formクラスについて】 フィールドに@Validがついています。 処理メソッドconfirm()に@Validがついているのに何でさらに@Validをつけるの?と疑問に思った方もいらっしゃるかもしれません。 引数につけた@Validはその引数に対して行うので、Form内につけたValidation用のアノテーションをチェックするだけです。 自動で内部のオブジェクトをネストしながらチェックはしません。 ですので、userフィールドのValidationを働かせるために@Validをつけました。 <モデル: com.sample.business.model.User>
import java.util.Date; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import org.hibernate.validator.constraints.Range; public class User { @NotNull @Size(min=3,max=3) private String id; @NotNull @Size(min=1) private String name; @NotNull @Range(min=10, max=99) private int age; private Date upDate; //更新日 public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Date getUpDate() { return upDate; } public void setUpDate(Date upDate) { this.upDate = upDate; } } 【NotNullなどのアノテーションについて】 すでに知っていおられる方もいらっしゃるかも知れませんが、妥当性チェックのためのものです。 NotNullは、値がnullのときにエラーにします。 <ビジネスロジック: com.sample.business.service.UserService>
import com.sample.business.model.User; import com.sample.dao.UserDao; //本当はインターフェースからimplemetsすべきですが、ここでは簡略のためそのままクラスを作っています。 public class UserService { private UserDao userDao; public UserDao getUserDao() { return userDao; } public void setUserDao(UserDao userDao) { this.userDao = userDao; } public User getUser(String id){ return this.userDao.getUser(id); } public void updateUser(User user){ this.userDao.updateUser(user); } } <Dao: com.sample.dao.UserDao>
import java.text.ParseException; import java.text.SimpleDateFormat; import com.sample.business.model.User; //本当はインターフェースからimplemetsすべきですが、ここでは簡略のためそのままクラスを作っています。 public class UserDao { public User getUser(String id){ //ここではハードコードしていますが、本当はDBから値を取得します。 User user = new User(); try { user.setId(id); user.setName("未華子"); user.setAge(21); SimpleDateFormat f =new SimpleDateFormat("yyyy/MM/dd"); user.setUpDate(f.parse("2012/04/01")); } catch (ParseException e) {} return user; } public void updateUser(User user){ //DBに値を更新する処理を書きます。ここでは省略。 } } <メッセージリソース(画面用): /src/messages-spring_ja.properties>
型変換エラーメッセージ用と、画面表示用のモデルのプロパティ名を設定します。 この記事では、妥当性チェックのエラーメッセージとファイルを分けました。一緒にしても良いと思います。
こちらのWEBの内容から引用させていただきました。 ちなみにメッセージソースは通常Spring設定ファイルで読み込ませる必要があります。 しかし、Hibernate Validatorではメッセージソースのファイル名を「ValidationMessages」にしておくと、自動で読み込みます。 ここでは自動読み込みを使用しており、以下のSpring設定ファイルでも記述していません。 もし、違うファイル名にしたい場合はSpring設定ファイルで「ResourceBundleMessageSource」などの読み込みクラスを設定し、 「LocalValidatorFactoryBean」のvalidationMessageSourceプロパティーに設定する必要があるようです。 <Spring設定ファイル: WebContent/WEB-INF/spring/applicationContext-webmvc.xml>
<context:component-scan base-package="com.sample.controller" /> <mvc:annotation-driven /> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/> <!-- View --> <bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> <property name="order" value="2"/> </bean> <!-- Declare the Interceptor --> <mvc:interceptors> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="locale" /> </mvc:interceptors> <!-- Declare the Resolver --> <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" > <property name="defaultLocale" value="ja" /> </bean> <!-- メッセージ --> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource" p:basenames="messages-spring" /> <!-- サービスとDaoの登録 --> <bean id="userService" class="com.sample.business.service.UserService"> <property name="userDao" ref="userDao"/> </bean> <bean id="userDao" class="com.sample.dao.UserDao"/> </beans> 【mvc:などのタグ】 後の記事で詳しく見ていきます。 ここでは重要な、context:component-scan、mvc:annotation-drivenだけ見てください。 この2つを記述することで、アノテーションベースのMVCを実現できます。 <JSPファイル(トップページ): WebContent/top.jsp>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>トップ</title> </head> <body> テスト<br> <a href="user/edit/input.html?user.id=001">Springテストへ</a><br> </body> </html> <JSPファイル(入力画面): WebContent/WEB-INF/jsp/user-Edit-Input.jsp>
【JSPタグについて】 spring:、form:などのJSPタグについては別の記事で詳しく見ていきます。 ここでは、Modelクラスに設定されたオブジェクトを結ぶものと思っていただければ十分です。
<JSPファイル(入力画面): WebContent/WEB-INF/jsp/user-Edit-Confirm.jsp>
<JSPファイル(入力画面): WebContent/WEB-INF/jsp/user-Edit-Finish.jsp>
<html> <body> 完了しました。 </body> </html> まとめ画面イメージ サンプルの画面のイメージは上記のとおりです。 型変換エラーも妥当性チェックエラーもちゃんと実行されています。 サンプルのコード量が多いのでコピーが大変かもしれません。すみません。 うまく動作してくれると嬉しいです。 Created Date: 2012/04/07 |