Как создавать и уничтожать управляемые компоненты CDI (Weld) через BeanManager?

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

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

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

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

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

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

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

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

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

7 голосов | спросил Ben Kirby 7 WedEurope/Moscow2011-12-07T21:51:45+04:00Europe/Moscow12bEurope/MoscowWed, 07 Dec 2011 21:51:45 +0400 2011, 21:51:45

3 ответа


0

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

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

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

ответил LightGuard 8 ThuEurope/Moscow2011-12-08T11:19:43+04:00Europe/Moscow12bEurope/MoscowThu, 08 Dec 2011 11:19:43 +0400 2011, 11:19:43
0

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

ответил Pete Muir 14 WedEurope/Moscow2011-12-14T14:03:50+04:00Europe/Moscow12bEurope/MoscowWed, 14 Dec 2011 14:03:50 +0400 2011, 14:03:50
0

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

ответил Readren 3 MarpmSun, 03 Mar 2013 13:11:31 +04002013-03-03T13:11:31+04:0001 2013, 13:11:31

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132