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>

まとめ

  • @MVC においては RedirectAttributes#addFlashAttribute メソッドを使用すると自動的に Flash Scope が使われる
  • その他の特別な設定は不要(!)

*1:2.5 より導入されたアノテーションベースのアプローチをこのように称することがあるようです。

*2:リンク先の記事では、Spring 3.0 で Flash Scope を先取りして実現する方法にも言及されており、大変有用です。