2011-12-07 13 views
7

インスタンス.select()。get()ではなく、BeanManagerを使用してCDIマネージドBeanのインスタンスを作成しようとしています。BeanManagerを使用してCDI(Weld)Managed Beanを作成および破棄する方法は?

これは、ApplicationScoped Beanとその扶養家族のガベージコレクションに関する問題の回避策として提案されています。CDI Application and Dependent scopes can conspire to impact garbage collection?のバックグラウンドとこの推奨される回避策を参照してください。

ApplicationScoped BeanでInstanceプログラムルックアップメソッドを使用すると、Instanceオブジェクトとそこから取得するすべてのBeanは最終的にApplicationScoped Beanに依存するため、ライフサイクルを共有します。 BeanManagerでBeanを作成すると、Beanインスタンス自体にハンドルがあり、明らかに明示的に破棄することができますが、これはGCanされることを意味します。

私の現在のアプローチはBeanManagerUtilクラス内でBeanを作成し、豆、インスタンス、およびCreationalContextの複合オブジェクトを返すことです。このことから

public class BeanManagerUtil { 

    @Inject private BeanManager beanManager; 

    @SuppressWarnings("unchecked") 
    public <T> DestructibleBeanInstance<T> getDestructibleBeanInstance(final Class<T> type, 
      final Annotation... qualifiers) { 

     DestructibleBeanInstance<T> result = null; 
     Bean<T> bean = (Bean<T>) beanManager.resolve(beanManager.getBeans(type, qualifiers)); 
     if (bean != null) { 
      CreationalContext<T> creationalContext = beanManager.createCreationalContext(bean); 
      if (creationalContext != null) { 
       T instance = bean.create(creationalContext); 
       result = new DestructibleBeanInstance<T>(instance, bean, creationalContext); 
      } 
     } 
     return result; 
    } 
} 

public class DestructibleBeanInstance<T> { 

    private T instance; 
    private Bean<T> bean; 
    private CreationalContext<T> context; 

    public DestructibleBeanInstance(T instance, Bean<T> bean, CreationalContext<T> context) { 
     this.instance = instance; 
     this.bean = bean; 
     this.context = context; 
    } 

    public T getInstance() { 
     return instance; 
    }  

    public void destroy() { 
     bean.destroy(instance, context); 
    } 
} 

を、呼び出し元のコードでは、私はそれから得ることができます実際のインスタンス、後の検索のためのマップに入れ、そして通常通り使用します。その上

private Map<Worker, DestructibleBeanInstance<Worker>> beansByTheirWorkers = 
    new HashMap<Worker, DestructibleBeanInstance<Worker>>(); 
... 
DestructibleBeanInstance<Worker> destructible = 
     beanUtils.getDestructibleBeanInstance(Worker.class, workerBindingQualifier); 
Worker worker = destructible.getInstance(); 
... 

私はそれで終わりだとき、私は(破壊ラッパーを検索して破壊する呼び出すことができます)、およびBeanとその扶養家族は清掃する必要があります:

DestructibleBeanInstance<JamWorker> workerBean = 
     beansByTheirWorkers.remove(worker); 
workerBean.destroy(); 
worker = null; 

しかし、いくつかの労働者を実行し、20分ほどのために私のJBoss(7.1.0.Alpha1-SNAPSHOT)を出た後、私は、GCはまだしかし、ショーをjmapのヒストグラムを

2011.002: [GC 
Desired survivor size 15794176 bytes, new threshold 1 (max 15) 
1884205K->1568621K(3128704K), 0.0091281 secs] 

を発生見ることができます古い労働者とその従属インスタンスがぶら下がり、unGCed。私は何が欠けていますか?

デバッグによって、作成されたBeanのコンテキストフィールドに、正しいWorkerタイプのコンテキストがあり、incompleteInstancesおよびparentDependentInstancesがないことがわかりました。それには従業員のフィールドから予想される数のdependentInstancesがあります。

Worker上のこれらのフィールドの1つは実際にはInstanceであり、このフィールドをプログラムインスタンスの参照によって取得されたWorkerのフィールドと比較すると、若干異なるCreationalContextメイクがあります。 BeanManagerから取得したWorkerのInstanceフィールドは、incompleteInstancesの下にあるワーカー自体を持っているため、WorkerのInstanceフィールドはインスタンスを経由して検索されます。どちらも同じparentDependentInstancesおよびdependentInstancesを持ちます。

これは、インスタンスの取得を正しく反映していないことを示唆しています。これが破壊の欠如に貢献していますか?

最後に、デバッグすると、DestructibleBeanInstance.destroy()でbean.destroy()が呼び出され、これがManagedBean.destroyに渡され、依存オブジェクトが.releaseの一部として破棄されることがわかります。 ()。しかし、彼らはまだガベージコレクションを取得していません!

これに関するお手伝いをさせていただきます。ありがとう。

答えて

2

貼り付けたコードでいくつか変更したいと思います。

  1. このクラスを通常のJavaクラスにして、注入しないでBeanManagerに渡します。何かがそんなに邪魔になるかもしれない。それはそうではないかもしれませんが、可能性があります。
  2. BeanManager.createCreationContext(null)を使用して新しいCreationalContextを作成します。このスコープは、従属スコープを提供します。このスコープは、完了したらCreationalContext.release()を呼び出して解放することができます。そのCreationalContextに他Beansはありませんと仮定するとあなたはすでにDestructibleBeanInstanceに持ってCreationalContextのリリースメソッドを呼び出すことによって、正しくあなたが望むように動作するようにすべてを取得することができるかもしれ

、その台無しにアプリケーションをでしょう。最初にそれを試してみてください。

+0

ありがとう、Jason。あなたが提案した変更を行ったが、まだガベージコレクションを見ていなかった。しかし、私が_full_ GCを待っていたとき、どちらのアプローチでもオブジェクトが収集されました - 成功!これは完全なGCの前のケースではありませんでした。 時間があれば、 '.createCreationContext(null)'と '.createCreationContext(bean)'の違いを説明できますか?私は拡張機能を書くためのドキュメントで前者を見てきましたが、タイプの 'bean'バージョン(私が意味するものを見れば)がまだ存在していなかったときのものだと思いました。助けてくれてありがとう。 –

+0

仕様によると、Beanの非コンテキストインスタンスが提供されるため、そのBeanを作成するために実行されたコードの部分以外の参照はありません。私はPete Muirにいくつかのさらなる洞察を求めたが、まだ聞いていない。 – LightGuard

2

Bean以外のクラスを注入する場合にのみnullを渡す必要があります。あなたの場合は、Beanを注入しています。しかし、私はまだGCがこの場合に動作すると期待していますので、テストケースと再現手順を使ってWeld問題トラッカーにJIRAを登録できますか?

1

問題を解決するには、動的プロキシを使用してBean破壊を処理することができます。プログラムでBeanクラスのインスタンスを取得するコードは次のようになります。

public static <B> B getBeanClassInstance(BeanManager beanManager, Class<B> beanType, Annotation... qualifiers) { 
    final B result; 
    Set<Bean<?>> beans = beanManager.getBeans(beanType, qualifiers); 
    if (beans.isEmpty()) 
     result = null; 
    else { 
     final Bean<B> bean = (Bean<B>) beanManager.resolve(beans); 
     if (bean == null) 
      result = null; 
     else { 
      final CreationalContext<B> cc = beanManager.createCreationalContext(bean); 
      final B reference = (B) beanManager.getReference(bean, beanType, cc); 
      Class<? extends Annotation> scope = bean.getScope(); 
      if (scope.equals(Dependent.class)) { 
       if (beanType.isInterface()) { 
        result = (B) Proxy.newProxyInstance(bean.getBeanClass().getClassLoader(), new Class<?>[] { beanType, 
          Finalizable.class }, new InvocationHandler() { 
         @Override 
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
          if (method.getName().equals("finalize")) { 
           bean.destroy(reference, cc); 
          } 
          try { 
           return method.invoke(reference, args); 
          } catch (InvocationTargetException e) { 
           throw e.getCause(); 
          } 
         } 
        }); 
       } else 
        throw new IllegalArgumentException("If the resolved bean is dependent scoped then the received beanType should be an interface in order to manage the destruction of the created dependent bean class instance."); 
      } else 
       result = reference; 
     } 
    } 
    return result; 
} 

interface Finalizable { 
    void finalize() throws Throwable; 
} 

このようにして、ユーザーコードは簡単になります。それは破壊の世話をする必要はありません。 この制限は、受信したBeanTypeがインタフェースではなく、解決されたBeanクラスが@Dependentである場合はサポートされていません。しかし、arroundを動作させるのは簡単です。インターフェイスを使用するだけです。 私はこのコードを(JBoss 7.1.1で)テストし、依存するステートフルセッションBeanについても動作します。

+0

優れたアイデア、ありがとう@Readren –

関連する問題