Как создавать и уничтожать управляемые компоненты CDI (Weld) через BeanManager? - PullRequest
7 голосов
/ 07 декабря 2011

Я пытаюсь создать экземпляры управляемых компонентов CDI, используя BeanManager, а не Instance .select (). Get ().

Это было предложено в качестве обходного пути к проблеме, с которой я столкнулся с компонентами ApplicationScoped и сборкой мусора их зависимых элементов - см. Область применения и зависимые области применения CDI могут влиять на сборку мусора? для фона, и это предложил обходной путь.

Если вы используете метод программного поиска Instance для bean-компонента ApplicationScoped, то объект Instance и любые бины, которые вы получаете от него, все в конечном итоге зависят от bean-компонента ApplicationScoped и, следовательно, совместно используют его жизненный цикл. Однако, если вы создаете bean-компоненты с BeanManager, у вас есть дескриптор самого экземпляра Bean, и, очевидно, вы можете явно уничтожить его, что, как я понимаю, означает, что он будет GCed.

Мой текущий подход заключается в создании компонента внутри класса BeanManagerUtil и возвращении составного объекта Bean, instance и 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 (), а бин и его зависимые объекты должны быть очищены:

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 по-прежнему показывает, что старые рабочие и их зависимые экземпляры слоняются без дела. Чего мне не хватает?

Посредством отладки я вижу, что контекстное поле созданного bean-компонента имеет контекстный контекст с правильным типом Worker, в котором нет incompleteInstances и parentDependentInstances. Он имеет несколько зависимых экземпляров, которые ожидаются от полей на работнике.

Одним из этих полей в Worker на самом деле является Instance, и когда я сравниваю это поле с полем Worker, полученным с помощью программного поиска Instance, они имеют немного другую структуру CreationalContext. Поле Instance на Worker, просмотренном с помощью Instance, имеет самого работника в незаконченных экземплярах, а поле Instance на Worker, полученное из BeanManager, - нет. Они оба имеют идентичные parentDependentInstances и зависимые экземпляры.

Это говорит о том, что я неправильно отразил получение экземпляра. Может ли это способствовать отсутствию разрушений?

Наконец, при отладке я вижу, как bean.destroy () вызывается в моем DestructibleBeanInstance.destroy (), и это проходит к ManagedBean.destroy, и я вижу, как зависимые объекты уничтожаются как часть .release ( ). Однако они все еще не собирают мусор!

Любая помощь по этому вопросу будет принята с благодарностью! Спасибо.

Ответы [ 3 ]

2 голосов
/ 14 декабря 2011

Передача в null должна быть сделана только тогда, когда вы вводите какой-то другой класс, кроме bean-компонента.В вашем случае вы вводите боб.Однако я все же ожидал бы, что в этом случае будет работать GC, поэтому не могли бы вы подать JIRA в трекер проблем со сваркой с помощью контрольного примера и шагов для воспроизведения?

2 голосов
/ 08 декабря 2011

Я бы изменил пару вещей в коде, который вы вставили.

  1. Сделайте этот класс обычным Java-классом, без внедрения и передачи в BeanManager. Что-то может испортить этот путь. Это маловероятно, но возможно.
  2. Создайте новый CreationalContext с помощью BeanManager.createCreationContext(null), который даст вам по существу зависимую область, которую вы можете освободить, когда закончите, позвонив CreationalContext.release().

Возможно, вы сможете заставить все работать правильно, как вам нужно, вызвав метод release для CreationalContext, который у вас уже есть в DestructibleBeanInstance, при условии, что в этом CreationalContext нет другого Beans, который бы испортил ваше приложение , Попробуйте сначала и посмотрите, не испортит ли это все.

1 голос
/ 03 марта 2013

Лучшим способом решения вашей проблемы может быть использование динамического прокси для обработки уничтожения бина.Код для получения экземпляра класса компонента должен быть программным:

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, не поддерживается.Но легко работать вокруг.Просто используйте интерфейс.Я протестировал этот код (с JBoss 7.1.1), и он работает также для зависимых сессионных компонентов с состоянием.

...