Spring Framework Advent Calendar 2011 part.4 - Spring MVC 3.1 で導入される Flash Scope
Spring Framework Advent Calendar の 4 日目です。今日は Spring MVC 3.1 の新機能について触れようと思います。
Spring MVC 3.1 では、@MVC*1 関連の機能がさらに強化されています。そのうちのひとつである Flash Scope について紹介します。なお、本記事の執筆時点では Spring 3.1 のバージョンは RC1 であり、正式リリース版ではありません。そのため、最終的なリリース版と挙動が異なる可能性があるかもしれません(可能性は限りなく低いですが)。
Flash Scope
Flash Scope とは、通常の Request Scope より少し長い生存期間を持つスコープです。通常の HTTP Request のひとつ先の Request の時点まで生存しています。主に、リダイレクト後のビューで一回だけメッセージを表示する目的などで使用されます(画面をリロードすると消える)。 Spring MVCでフラッシュスコープの機能を簡単に実装する方法 - 達人プログラマーを目指して に、もう少し詳しい解説がありますので、併読されると良いと思います*2。
実装例
それでは、実際の例を見てみます。以下は適当に作った Controller クラスです。
@Controller public class ApplicationController { @RequestMapping("/") public String index() { return "index"; } @RequestMapping(value = "/register", method = RequestMethod.GET) public String registerInput(Model model) { if (!model.containsAttribute("registerForm")) { model.addAttribute(new RegisterForm()); } return "register/input"; } @RequestMapping(value = "/register", method = RequestMethod.POST) public String register(@Valid RegisterForm form, BindingResult result, RedirectAttributes attrs) { if (result.hasErrors()) { return "register/input"; } attrs .addAttribute("id", form.getName()) .addFlashAttribute("message", "登録されました."); return "redirect:/register"; } }
注目すべきは、最後のメソッドです。
@RequestMapping(value = "/register", method = RequestMethod.POST) public String register(@Valid RegisterForm form, BindingResult result, RedirectAttributes attrs) { if (result.hasErrors()) { return "register/input"; } attrs .addAttribute("id", form.getName()) .addFlashAttribute("message", "登録されました."); return "redirect:/register"; }
メソッドの引数に RedirectAttributes とあります。これが、Spring 3.1 から追加されたインターフェースです。位置づけは org.springframework.ui.Model インターフェースの拡張になっています。
Spring 3.1 において、リダイレクトに関するパラメーターは、このインターフェースを通してフレームワークに通知することができます。そして、通知の際に addAttribute メソッドを使用すれば通常のURLパラメーターとして扱われ、addFlashAttribute メソッドを使用すれば Flash Scope として扱われる――つまり、リダイレクト後の画面まで引き継がれるパラメーターとなります。この機構は Spring MVC 本体に組み込まれているため、Flash Scope を利用する際に特別な Bean 宣言をする必要もありません。
なお、addAttribute や addFlashAttribute メソッドは、インスタンス自身を return するように設計されています。いわゆる「流れるようなインターフェース」で、メソッドチェーンを使用して連続で add の操作を行うことができるようになっています。細かい点ではありますが、嬉しいですね。
実際の画面で見てみましょう。簡単な入力フォームです。これは、先ほど登場した Controller に対応する画面です。
名前の欄に、適当な値を入力します。
入力後、「登録」を押すと、結果が表示されます。
ここで注意してほしいのは、id だけが URL のパラメーターとして表示されており message のほうは表示されていないことです。
では、画面を F5 等でリロードしてみます。POST したページであれば、リロードをかけようとすると、ブラウザによって「フォームを再送信しますか?」というような内容のダイアログが表示されますが、いまはリダイレクト後の画面ですので、もちろん表示されません。
ご覧の通り、message のほうは消えてしまいます。message のスコープは Flash Scope であることが確認できました。
参考までに、画面の HTML (正確には JSP) のソースを掲載しておきます。
<%@ page contentType="text/html; charset=utf-8" trimDirectiveWhitespaces="true" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <style type="text/css"> .inputForm .field { margin: 1em; } .inputForm .button ul li { list-style-type: none; float: left; margin: 0.5em; } </style> <div class="inputForm"> <form:form modelAttribute="registerForm" method="post"> <div class="field"> <div class="label">名前</div> <div class="form"><form:input path="name" /></div> <form:errors path="name" /> </div> <div class="field"> <div class="label">生年月日</div> <div class="form"><form:input path="birthday" /></div> <form:errors path="birthday" /> </div> <div class="button"> <input type="submit" name="execute" value="登録" /> </div> </form:form> </div> <p><c:if test="${not empty param['id']}">id=<c:out value="${param['id']}" /></c:if></p> <p><c:if test="${not empty message}">message=<c:out value="${message}" /></c:if></p> <p><a href="<spring:url value="/" />">戻る</a></p>