BeanリファレンスとCreationalContext (1)
2011/09/25 コメントを残す
CreationalContextとは
長くなるので、これから何回かに分けてCreationalContextについて書こうと思います。
インタフェース javax.enterprise.context.spi.CreationalContext はContexual実装によってインスタンスの生成と破棄の間に使われるオペレーションを提供します。このCreationalContextのインスタンスはBeanのインスタンスが生成されるときに、メソッド引数として引き渡されるものです。
public interface CreationalContext<T> { public void push(T incompleteInstance); public void release(); }
このCreationalContextの役割を理解するため、Beanインスタンスの生成のプロセスを復習してみましょう。このブログのBean<MyBean>の定義を見てください。MyBean::create()メソッドの引数として渡されたCreationalContextは、メソッド実装内部で呼び出されるproduce()やinject()のメソッド引数としても引き渡されています。
public MyBean create(CreationalContext<MyBean> ctx) { log("Bean<MyBean>::create"); MyBean instance = it.produce(ctx); it.inject(instance, ctx); it.postConstruct(instance); return instance; }
Beanインスタンス生成とCreationalContextとの間の関連
コンテキストへのアクセスからBean(つまりContexual)の生成、インジェクションまでの処理の流れを整理すると次のようになります。
1. Context::get(Contextual<T> contextual, CreationalContext<T> creationalContext)
2. Contexual::create(CreationalContext<T> ctx)
2-1. InjectionTarget::produce(CreationalContext<T> ctx)
2-1-1 Bean::createInstance(CreationalContext<T> ctx)
2-1-2 CreationalContext::push(T instance)
2-2 InjectionTarget::inject(T instance, CreationalContext<T> ctx)
1: コンテキストに対してget()を呼び出します
2: もしコンテキストに当該Beanが登録されていなければBeanインスタンスを生成します。
2-1: Beanインスタンスを生成するためInjectionTarget::produce()を呼び出します。
2-1-1: produce()の内部で、Beanインスタンスを生成します。
2-1-2: 生成したBeanインスタンスをCreationalContext::push()します。
2-2: Beanインスタンスのフィールドやメソッドでのインジェクションを実行するためInjectionTaget::inject()を呼び出します
上の処理の流れからわかることは、2-1-2でContextにpushしたCreationContextインスタンスがinject()のメソッド引数に渡されることによって、inject()の実装がこれからインジェクトしようとしているBeanインスタンスがすでに生成されたものであるか否かを判断できるということです。
CDI仕様書が言っていること
CDI 1.0仕様書の6.1.1. The CreationalContext interfaceを見るとpush()の説明が次のように書いてあります。
The implementation of Contextual is not required to call push(). However, for certain bean scopes, invocation of push() between instantiation and injection helps the container minimize the use of client proxy objects (which would otherwise be required to allow circular dependencies). 訳:Contextualの実装はpush()を呼び出す必要はありません。しかし、Beanスコープによっては、生成とインジェクションの間でpush()を呼び出すことによって、コンテナがクライアントプロキシオブジェクトを呼び出す回数を最小化することができます(さもなければ、循環した依存関係を認める必要があります)。
これは、inject()実装がCreationalContextを利用することでクライアントプロキシを生成しない場合があるということを暗に言っています。 inject()がやっていることは、@Injectのフィールドやメソッドに対してクライアントプロキシを設定することです(この説明はこの日のブログを見てください)。
クライアントプロキシを生成する手段はBeanManagerの以下のメソッドで提供されています。
- getInjectableReference(InjectionPoint ij, CreationalContext<?> ctx)
Weld 1.1.2実装では、BeanManagerインタフェースの実装はBeanManagerImplクラスが提供していますので、そのメソッド実装を確認すれば仕様書が意図していることがわかるかもしれません。次回は、実際にソースコードを調べてみましょう。