0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SessionスコープのBeanを利用しようとしたらハマった話

Posted at

はじめに

SessionスコープのBeanを利用したかったので、@SessionScopeを使った実装をしたところ、デッドループが発生してしまったので、その原因と解決方法を調べた際の記録です。

SessionスコープのBeanを利用したかったが、デッドループが発生

SessionスコープのBeanを利用して、そのSession中で有効なBean(例えば、ECサイトのショッピングカート)を作るため、@Component@SessionScopeを付与したクラス(仮に「Cart」クラスとします。)を作成しました。
そして、セッション開始時にCartクラスが持つカートの中の商品を表すリストitemListを初期化するため、HttpSessionListenerにおいてArrayListを生成して初期化をしようとしました。
ところが、その初期化をする処理が何度も呼び出され(HttpSessionListenersessionCreatedが何度も呼び出され)、デッドループが発生してしまいました。

原因はプロキシ?

ここでかなりハマって色々と調べてみると、原因はSessionスコープのBeanをラップするプロキシが関係しているのでは、ということが分かりました。
SessionスコープのBeanをインジェクションするときに、実際のCartクラスのインスタンスが注入されるのではなく、それをラップするプロキシが注入されます。
そして、プロキシを介してCartクラスが呼び出されます。
その際、セッション内でまだ完全に初期化されていないCartクラスを呼び出すと、プロキシはCartの生成を行います。
その状態でCartクラスを参照する処理が行われると、まだCartクラスが完全に初期化されていないため、プロキシが再度Cartを生成し、ということを繰り返してしまい、デッドループに陥る、という仕組みのようです。
まだ理解が浅いですが、上記がデッドループの原因と考えられます。

解決策

セッションスコープを使用せず、HttpSessionに直接Cartインスタンスを格納します。
具体的には、ユーザが最初にアクセスするページのコントローラにおいて、Cartがセッションに格納されていなければ(初回のアクセスであれば)、新しくCartをnewし、それをHttpSessionのsetAttribute()メソッドを用いてセッションに格納します。
これで、Springのプロキシを使用しなくて良いため、デッドループが解消されます。

おわりに

Springのスコープ管理がなかなか掴めません…セッションスコープのBeanなので、セッション開始時に初期化した方がきれいかなと思い、最初の方法で進めたかったのですが、最終的にコントローラにベタ書きすることになりました。
もし他に良い方法があれば、ご教示いただきたいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?