簡體   English   中英

如何通過BeanManager創建和銷毀CDI(Weld)托管Bean?

[英]How to create and destroy CDI (Weld) Managed Beans via the BeanManager?

我正在嘗試使用BeanManager而不是Instance .select()。get()創建CDI托管bean的實例。

這被建議作為我已經使用ApplicationScoped bean和他們的家屬的垃圾收集的問題的解決方法 - 請參閱CDI應用程序和依賴范圍可以合謀影響垃圾收集? 對於背景和建議的解決方法。

如果在ApplicationScoped bean上使用Instance編程查找方法,Instance對象和從中獲取的任何bean最終都依賴於ApplicationScoped bean,因此共享它的生命周期。 但是,如果使用BeanManager創建bean,則會在Bean實例本身上設置句柄,並且顯然可以明確地銷毀它,我理解這意味着它將被GCed。

我目前的方法是在BeanManagerUtil類中創建bean,並返回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();
...

當我完成它時,我可以查找可破壞的包裝器並在其上調用destroy(),並且應該清理bean及其依賴項:

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

然而,在運行幾個工作人員並離開我的JBoss(7.1.0.Alpha1-SNAPSHOT)20分鍾后,我可以看到GC發生

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

然而,JMAP直方圖仍然顯示舊工人及其依賴的實例,unGCed。 我錯過了什么?

通過調試,我可以看到創建的bean的上下文字段具有正確的Worker類型的上下文,沒有incompleteInstances,也沒有parentDependentInstances。 它有許多dependentInstances,它們與worker上的字段一樣。

Worker上的其中一個字段實際上是一個實例,當我將此字段與通過編程實例查找檢索到的Worker字段進行比較時,它們的CreationalContext組成略有不同。 通過Instance查找的Worker上的Instance字段將worker本身置於incompleteInstances下,而從BeanManager中檢索的Worker上的Instance字段則沒有。 它們都具有相同的parentDependentInstances和dependentInstances。

這告訴我,我沒有正確反映實例的檢索。 這會導致缺乏破壞嗎?

最后,在調試時,我可以看到在我的DestructibleBeanInstance.destroy()中調用bean.destroy(),這會進入ManagedBean.destroy,我可以看到依賴對象被作為.release()的一部分被銷毀。 但是他們仍然沒有收集垃圾!

任何有關這方面的幫助將非常感謝! 謝謝。

我會在你粘貼的代碼中改變一些東西。

  1. 使該類成為常規java類,無需注入並傳入BeanManager。 有點像這樣的東西搞亂了。 這不太可能,但可能。
  2. 使用BeanManager.createCreationContext(null)創建一個新的CreationalContext,它將為您提供一個依賴的作用域,當您通過調用CreationalContext.release()完成時可以釋放該作用域。

您可以通過在DestructibleBeanInstance已經擁有的CreationalContext上調用release方法,使所有內容按照您想要的方式正常工作,假設CreationalContext中沒有其他Beans會弄亂您的應用程序。 首先嘗試一下,看看它是否會混亂。

解決問題的更好方法可能是使用動態代理來處理bean破壞。 獲取bean類實例programaticaly的代碼將是:

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;
}

這樣用戶代碼就更簡單了。 它沒有必要照顧破壞。 此approuch的限制是不支持接收的beanType不是接口且解析的bean類是@Dependent情況。 但是很容易在周圍工作。 只需使用界面。 我測試了這段代碼(使用JBoss 7.1.1),它也適用於依賴的有狀態會話bean。

只有在注入除bean之外的某些類時,才能傳入null。 在你的情況下,你正在注入一個bean。 但是我仍然期望GC能夠在這種情況下工作,那么您是否可以在Weld問題跟蹤器中使用測試用例和重現步驟提交JIRA?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM