简体   繁体   English

如何通过BeanManager创建和销毁CDI(Weld)托管Bean?

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

I'm trying to create instances of CDI managed beans using the BeanManager rather than Instance .select().get(). 我正在尝试使用BeanManager而不是Instance .select()。get()创建CDI托管bean的实例。

This was suggested as a workaround to an issue I've been having with ApplicationScoped beans and garbage collection of their dependents - see CDI Application and Dependent scopes can conspire to impact garbage collection? 这被建议作为我已经使用ApplicationScoped bean和他们的家属的垃圾收集的问题的解决方法 - 请参阅CDI应用程序和依赖范围可以合谋影响垃圾收集? for background and this suggested workaround. 对于背景和建议的解决方法。

If you use the Instance programmatic lookup method on an ApplicationScoped bean, the Instance object and any beans you get from it are all ultimately dependent on the ApplicationScoped bean, and therefore share it's lifecycle. 如果在ApplicationScoped bean上使用Instance编程查找方法,Instance对象和从中获取的任何bean最终都依赖于ApplicationScoped bean,因此共享它的生命周期。 If you create beans with the BeanManager, however, you have a handle on the Bean instance itself, and apparently can explicitly destroy it, which I understand means it will be GCed. 但是,如果使用BeanManager创建bean,则会在Bean实例本身上设置句柄,并且显然可以明确地销毁它,我理解这意味着它将被GCed。

My current approach is to create the bean within a BeanManagerUtil class, and return a composite object of Bean, instance, and CreationalContext: 我目前的方法是在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);
    }
}

From this, in the calling code, I can then get the actual instance, put it in a map for later retrieval, and use as normal: 从这里,在调用代码中,我可以获得实际的实例,将其放在地图中供以后检索,并正常使用:

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

When I'm done with it, I can lookup the destructible wrapper and call destroy() on it, and the bean and its dependents should be cleaned up: 当我完成它时,我可以查找可破坏的包装器并在其上调用destroy(),并且应该清理bean及其依赖项:

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

However, after running several workers and leaving my JBoss (7.1.0.Alpha1-SNAPSHOT) for 20 minutes or so, I can see GC occurring 然而,在运行几个工作人员并离开我的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]

Yet a JMAP histogram still shows the old workers and their dependent instances hanging around, unGCed. 然而,JMAP直方图仍然显示旧工人及其依赖的实例,unGCed。 What am I missing? 我错过了什么?

Through debugging, I can see that the context field of the bean created has the contextual of the correct Worker type, no incompleteInstances and no parentDependentInstances. 通过调试,我可以看到创建的bean的上下文字段具有正确的Worker类型的上下文,没有incompleteInstances,也没有parentDependentInstances。 It has a number of dependentInstances, which are as expected from the fields on the worker. 它有许多dependentInstances,它们与worker上的字段一样。

One of these fields on the Worker is actually an Instance, and when I compare this field with that of a Worker retrieved via programmatic Instance lookup, they have a slightly different CreationalContext makeup. Worker上的其中一个字段实际上是一个实例,当我将此字段与通过编程实例查找检索到的Worker字段进行比较时,它们的CreationalContext组成略有不同。 The Instance field on the Worker looked up via Instance has the worker itself under incompleteInstances, whereas the Instance field on the Worker retrieved from the BeanManager doesn't. 通过Instance查找的Worker上的Instance字段将worker本身置于incompleteInstances下,而从BeanManager中检索的Worker上的Instance字段则没有。 They both have identical parentDependentInstances and dependentInstances. 它们都具有相同的parentDependentInstances和dependentInstances。

This suggests to me that I haven't mirrored the retrieval of the instance correctly. 这告诉我,我没有正确反映实例的检索。 Could this be contributing to the lack of destruction? 这会导致缺乏破坏吗?

Finally, when debugging, I can see bean.destroy() being called in my DestructibleBeanInstance.destroy(), and this goes through to ManagedBean.destroy, and I can see dependent objects being destroyed as part of the .release(). 最后,在调试时,我可以看到在我的DestructibleBeanInstance.destroy()中调用bean.destroy(),这会进入ManagedBean.destroy,我可以看到依赖对象被作为.release()的一部分被销毁。 However they still don't get garbage collected! 但是他们仍然没有收集垃圾!

Any help on this would be very much appreciated! 任何有关这方面的帮助将非常感谢! Thanks. 谢谢。

I'd change a couple of things in the code you pasted. 我会在你粘贴的代码中改变一些东西。

  1. Make that class a regular java class, no injection and pass in the BeanManager. 使该类成为常规java类,无需注入并传入BeanManager。 Something could be messing up that way. 有点像这样的东西搞乱了。 It's not likely, but possibly. 这不太可能,但可能。
  2. Create a new CreationalContext by using BeanManager.createCreationContext(null) which will give you essentially a dependent scope which you can release when you're done by calling CreationalContext.release() . 使用BeanManager.createCreationContext(null)创建一个新的CreationalContext,它将为您提供一个依赖的作用域,当您通过调用CreationalContext.release()完成时可以释放该作用域。

You may be able to get everything to work correctly the way you want by calling the release method on the CreationalContext you already have in the DestructibleBeanInstance , assuming there's no other Beans in that CreationalContext that would mess up your application. 您可以通过在DestructibleBeanInstance已经拥有的CreationalContext上调用release方法,使所有内容按照您想要的方式正常工作,假设CreationalContext中没有其他Beans会弄乱您的应用程序。 Try that first and see if it messes things up. 首先尝试一下,看看它是否会混乱。

A nicer way solve your problem could be to use a dynamic proxy to handle the bean destruction. 解决问题的更好方法可能是使用动态代理来处理bean破坏。 The code to obtain a bean class instance programaticaly would be: 获取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;
}

This way the user code is simpler. 这样用户代码就更简单了。 It doesnt have to take care of the destruction. 它没有必要照顾破坏。 The limitation of this approuch is that the case when the received beanType isn't an interface and the resolved bean class is @Dependent is not supported. 此approuch的限制是不支持接收的beanType不是接口且解析的bean类是@Dependent情况。 But is easy to work arround. 但是很容易在周围工作。 Just use an interface. 只需使用界面。 I tested this code (with JBoss 7.1.1) and it works also for dependent stateful session beans. 我测试了这段代码(使用JBoss 7.1.1),它也适用于依赖的有状态会话bean。

Passing in null should only be done when you injecting some class other than a bean. 只有在注入除bean之外的某些类时,才能传入null。 In your case, you are injecting a bean. 在你的情况下,你正在注入一个bean。 However I would still expect GC to work in this case, so could you file a JIRA in the Weld issue tracker with a test case and steps to reproduce? 但是我仍然期望GC能够在这种情况下工作,那么您是否可以在Weld问题跟踪器中使用测试用例和重现步骤提交JIRA?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM