Spring Framework Advent Calendar 2011 part.6 - 同一リクエスト内で同じ値を返す Date の実装
Spring Framework Advent Calendar の 6 日目です。本日は夜に更新時間が取れないため、前倒しでの投稿です。内容も簡易版となっております。
さて、時間もあまりないので単刀直入にいきます。同一リクエスト内で同じ値を返す実装と云うのは、つまりこういうことです。
/** * システム日付を返すインターフェースです。 */ public interface DateContext { /** * システムの現在日時を返します。 * @return 現在日時 */ public Date now(); }
/** * リクエストスコープにバインドされた {@link DateContext} です。 * 同一のリクエスト内では、常に同じ時刻を指します。 */ @Service @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.INTERFACES) public class RequestScopeDateContext implements DateContext { public static final String NAME = "requestScopeDateContext"; private Date now; /** * メソッドを呼び出したリクエストスコープ内で同一の現在日時を返します。 */ @Override public synchronized Date now() { if (now == null) { now = new Date(); } return now; } }
/** * {@link DateContext} のデフォルト実装。 */ @Service public class DefaultDateContext implements DateContext { /** * メソッドを呼び出した時点での現在日時を返します。 * @return 現在日時 */ @Override public Date now() { return new Date(); } }
ScopedProxy を利用して RequestScope と Bean のライフサイクルを結びつけている例です。この方式の良いところは、
- 「毎回、そのときのシステム時刻を返す DateContext」「同一リクエスト内では同じシステム時刻を返す DateContext」といった、時刻を返す機能は同じでも、異なる動作が求められるようなコンポーネントを Bean 定義ファイルやアノテーションで比較的簡単に交換できること。
- テスト時にモックとの差し替えが容易なこと。
- 内部で return new Date(); とかやってるコードをテストするのはなかなかしんどい。*1
といったところでしょうか。
なお、@Scope をすでに利用している方は既知の情報ですが、@Scope("request") や @Scope("session") を利用するには web.xml の listener 定義でorg.springframework.web.context.request.RequestContextListener を指定しておく必要があります。
<listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener>
利用する側は、単に DateContext を利用したいサービスに DI してやるだけです。簡単ですね。
で、こんなものが何の役に立つのかという話なのですが、複数のテーブルを同一リクエスト内で更新するときに、更新日時をすべて同じにしたい、といったケースがあったりします。そんなときに、多少役に立つかもしれません。