web-dev-qa-db-ja.com

CDI管理Beanインスタンスを取得する正規の方法:BeanManager#getReference()とContext#get()

Bean<T>に基づいて作成される)Class<T>のみで開始する場合、 BeanManager を介して自動作成されたCDI管理Beanインスタンスを取得するには、2つの一般的な方法があると考えました。

  1. BeanManager#getReference() の場合、スニペットでより頻繁に表示されます。

    Bean<TestBean> bean = (Bean<TestBean>) beanManager.resolve(beanManager.getBeans(TestBean.class));
    TestBean testBean1 = (TestBean) beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));
    
  2. Context#get() の場合、スニペットにはあまり表示されません。

    Bean<TestBean> bean = (Bean<TestBean>) beanManager.resolve(beanManager.getBeans(TestBean.class));
    TestBean testBean2 = beanManager.getContext(bean.getScope()).get(bean, beanManager.createCreationalContext(bean));
    

実際には、これらは最終的にはまったく同じことを行います。現在のCDI管理Beanインスタンスへのプロキシされた参照を返し、スコープにまだ存在しない場合はBeanインスタンスを自動作成します。

ただし、これは少し異なります。BeanManager#getReference()は常にまったく新しいプロキシインスタンスを作成しますが、以前に作成されている場合、Context#get()は既存のプロキシインスタンスを再利用します。これは、上記のコードが既存のTestBeanインスタンスのアクションメソッドで実行されたときに明らかです。

System.out.println(testBean1 == testBean2); // false
System.out.println(testBean1 == this); // false
System.out.println(testBean2 == this); // true

Context#get()の-​​ javadoc は、次のように非常に明確です。

特定のコンテキストタイプの既存のインスタンスを返すか、Contextual.create(CreationalContext)を呼び出して新しいインスタンスを作成し、新しいインスタンスを返します。

BeanManager#getReference()の-​​ javadoc はこれについて十分に明示されていません:

特定のBeanおよびそのBeanの特定のBeanタイプのコンテキスト参照を取得します。

これは私を混乱させました。どちらを使うのですか?どちらの方法でも、とにかくBean<T>インスタンスが必要であり、そこからBeanクラスとBeanスコープをすぐに利用できます。追加の引数として必要です。この特定のケースで外部から供給される必要がある理由は想像できません。

Context#get()は、まったく同じ基礎となるBeanインスタンスを参照する別のプロキシインスタンスを不必要に作成せず、既存のプロキシインスタンスを見つけて再利用するだけなので、よりメモリ効率が良いと想像できます。

これは私に次の質問をさせます:BeanManager#getReference()Context#get()よりも正確にいつ役立つのですか?スニペットで表示されることが多く、解決策として推奨されることが多いですが、すでに存在する場合でも、不必要に新しいプロキシを作成するだけです。

38
BalusC

beanManager#getReferenceはクライアントプロキシの新しいインスタンスを提供しますが、クライアントプロキシは特定のコンテキストの現在のコンテキストインスタンスにメソッド呼び出しを転送します。プロキシを取得して保持すると、現在のインスタンス(現在のリクエストなど)でメソッド呼び出しが呼び出されます。また、コンテキストインスタンスがシリアル化可能でない場合にも役立ちます。クライアントプロキシは、逆シリアル化した後に再接続されます。

BeanManager#getContextクライアントプロキシなしでターゲットインスタンスを取得します。クラス名にWeldのプロキシが表示される場合がありますが、これはインターセプトと装飾を提供する拡張サブクラスです。 Beanが傍受も装飾もされていない場合、これは指定されたBeanの単純なインスタンスになります。

通常、(1)は、ターゲットインスタンスに直接アクセスする必要がある(たとえば、そのフィールドにアクセスする)特別なユースケースがない限り、より適しています。

または言い換えれば

1)BeanManager#getReferenceは、Beanの通常のスコーププロキシを使用して「コンテキスト参照」を返します。 Beanに_@SessionScoped_が

_@SessionScoped User user;
_

次に、コンテキスト参照ユーザーは、各呼び出しのcurrentセッションのそれぞれのユーザーインスタンス(「コンテキストインスタンス」)を「ポイント」します。 2つの異なるWebブラウザーからのuser.getName()の2つの異なる呼び出しは、異なる答えを与えます。

2)Context#get()は、通常のスコーププロキシなしで内部の「コンテキストインスタンス」を返します。これは通常、ユーザーが自分で呼び出す必要があるものではありません。その方法で "Bob"の_User user_を取得し、それを_@ApplicationScoped_ Beanまたは静的変数に格納すると、ユーザーからの "Bob"のままになります。他のブラウザ!プロキシされていない直接のインスタンスを取得します。

36
Asif Bhutto

参照を取得するためにgetReference()メソッドを使用していたシングルトンがあります。シングルトンはすでに初期化されていますが、getReference()が使用されるたびに、getReference()によって作成されたプロキシが@PostConstructを呼び出しました。

@Startup
@ApplicationScoped
@Singleton

@PostConstruct
private void initialize() {}

GetContext()。get()メソッドに切り替えることで、不要な@PostConstructプロキシ呼び出しが行われなくなりました。

1
tinman13

これは、CDIをjavafxと統合するときに非常に役立ちました。依存するスコープのプロキシではなく、正しいスコープのオブジェクトの原因への参照が必要だったということです...

プロデューサーメソッドを使用して、次のようにコントローラーに注入されるjavaFX Node=を取得します。

@Inject
@ApplicationScoped
@FXMLFile("javafx/wares.fxml")
@FXMLController(WaresController.class)
Parent wares;

しかし、BeanManager#getReference()プロキシを使用すると、FXMLLoaderによって設定されたすべての値が「取得」され、getContext.get()によって解決されます。

これのThnx

0
brammie