コンポーネントからより狭いスコープのコンポーネントを使う
Seasar2では、コンテナに登録されているコンポーネントに対して、それより狭いスコープのコンポーネントをインジェクションすることはできません。
広いスコープのコンポーネントに対して、それより狭いスコープのコンポーネントをインジェクションすると、強参照により、狭いスコープのコンポーネントが消せなくなってしまうことが理由でしょう。
例えば、singletonスコープのコンポーネントAにsessionスコープのコンポーネントBをインジェクトできてしまうと、コンポーネントAからコンポーネントBに強参照が張られて、HttpSessionが解放されるタイミングになっても、コンポーネントBがガーベージコレクトされないことになってしまいます。
しかし、それらをインジェクションしなくても良いから、使いたいということはあります。
その場合は、singletonスコープで以下のようなインタフェースと実装を作ります。
public interface SessionScopeRepository { Object getComponent(Object componentKey); } public class SessionScopeRepositoryImpl implements SessionScopeRepository { public Object getComponent(Object componentKey) { // コンポーネント定義を見て、sessionスコープで // 登録されていることをバリデーションするといいかも return this.container.getComponent(componentKey); } private S2Container container; public void setContainer(S2Container container_) { this.container = container_; } public S2Container getContainer() { return this.container; } }
このコンポーネントを経由して、よりスコープの狭いコンポーネントにアクセスできます。
こんな感じです。
public class SingletonComponent { public void doSomething() { // sessionスコープのコンポーネントを取得 // メソッド内の変数に持つので、強参照はすぐ消える SessionComponent sessionComponent = getSessionScopeRepository().getComponent(SessionComponent.class); // アクセス sessionComponent.doSomething(); } // セッターインジェクションされるようにしておく private SessionScopeRepository sessionScopeRepository; public void setSessionScopeRepository(SessionScopeRepository repository) { this.sessionScopeRepository = repository; } public SessionScopeRepository getSessionScopeRepository() { return this.sessionScopeRepository; } }
SessionScopeRepositoryのように一段噛まさず、そのままS2Containerから取る手もありますが、
- 各コンポーネントがS2Containerと密結合になること
- バリデーションを掛けられないこと
を考えると、上記のようにラップする方がいい気がします。