ある日の出来事
SpringのBeanのスコープ、便利ですよね。ライフサイクル管理を任せられるのはDIコンテナを利用するメリットの大きなところだと思います。
いつも私は、何も考えずに以下のようにコーディングして、Springコンテナにスコープ管理を任せていました。
@Component
@SessionScope
public class User implements Serializable {}
そしてある日、ふと疑問が…このBeanって結局どこに格納されてるんだろう…@SessionScopeっていうくらいだからセッションに格納される?
結論
実行環境がServletの場合、
- @SessionScopeはHttpSessionに格納される
- @RequestScopeはHttpServletRequestに格納される
Spring WebFluxの場合、@SessionScopeや@RequestScopeはサポートされていない。いずれもスレッドローカルに値を管理する仕組みであり、実行スレッドが固定されない環境では実現が難しいため。
参考 stack overflow
我々は真相に迫った
検証環境
pom.xml
 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
                ..
    <\dependencies>
まずはデバッグ
アノテーションはただのマーカーなので、実際にアノテーションを処理するクラスを探したい。しかし、アノテーション自体にはブレークポイントが打てないため、探すのがけっこう難しい。そのため、まずはHttpSession#setAttribute(String name, Object value)にあたりを付けて、ブレークポイントを打ってみた。
そうすると、以下のスタックトレースが取得できた。

うーん、SessionScopeクラスがあやしそう。
Javadocを読んでみる
SessionScope(@SessionScopeとは違うパッケージの)はScopeインタフェース(@Scopeとは違う)を実装したクラスのひとつ。 またScopeインタフェースは、データのCRUD操作を定義している。
参考 SessionScope Javadoc
参考 Scope Javadoc
ソースコードを読んでみる
Scopeは4つのメソッドを持っている。
public interface Scope {
  Object get(String name, ObjectFactory<?> objectFactory);
  Object remove(String name);
  Object resolveContextualObject(String key);
  String getConversationId();
}
Scope#getの実装はAbstractRequestAttributesScope#getで以下のように定義されている。
参考 AbstractRequestAttributesScope#get
永続先への具体的な操作はRequestAttributesクラスで行われる。デバッグしてみたら実装クラスにはServletRequestAttributesが利用されていた。
ServletRequestAttributesを確認すると、HttpSession#setAttributeを呼んでいた。
参考 ServletRequestAttributes
@RequestScopeも似た処理で、書き込み先はHttpServletRequestだった。
終わりに
Springの@RequestScopeや@SessionScopeはどこから来てどこへ行くのか?我々は真相に迫ったが、あまり驚きはなかった。また、調べたことがそのままspringのリファレンスに書いてあった。リファレンスしっかり読もう…(反省)
参考 1.5.5. Custom scopes