When is a @Dependent scoped CDI bean destroyed, if you obtain that bean via Provider.get()?

Chris Rankin picture Chris Rankin · Jul 18, 2014 · Viewed 11.2k times · Source

I am struggling to understand the effective lifecycle of a @Dependent scoped bean in both CDI 1.0 and CDI 1.1. My experiments so far have lead me to the following conclusions:

  • A @Dependent scoped bean is not proxied.
  • No @PreDestroy method is invoked when a @Dependent bean is destroyed.
  • Provider.get() always creates a new instance of a @Dependent bean.
  • With JBoss 6/CDI 1.0, a @Dependent bean that is created by an @ApplicationScoped bean's Provider<> field is "leaked", because it still "belongs" to the Provider.
  • I have seen no evidence (yet!) of @Dependent beans being leaked by similar Providers when using WELD 2.1.2.Final/CDI 1.1. (Although this might be because these particular @Dependent beans are created by @Produces methods...!)

I see that CDI 1.1 has added a destroy() method to Instance<>, presumably to address the memory leak in CDI 1.0. But what about Provider<> - does that still leak in CDI 1.1? (And if it does, then how are you supposed to use Provider.get()?)

Basically, I have several @ApplicationScoped beans / @Singleton EJBs that I @Inject Provider fields into, and I am trying to use Provider.get() as factories for both @Dependent and @RequestScoped "helper" beans. I definitely do not want these beans to "belong" to their Provider fields, as I need the beans to be garbage collected afterwards:

public void updateStuff() {
    Helper helper = helperProvider.get();
    // use helper...
}

For my CDI 1.0 application, I was thinking of fixing the memory leak by "faking" my Providers with code like this:

provider = new Provider<MyBean>() {
    @Override
    public MyBean get() {
        return getReferenceFor(MyBean.class);
    }
};

private <T> T getReferenceFor(Class<T> type) {
    Bean<?> bean = beanManager.resolve(beanManager.getBeans(type));
    CreationalContext<?> context = beanManager.createCreationalContext(bean);
    try {
        return (T) beanManager.getReference(bean, bean.getBeanClass(), context);
    } finally {
        // Destroy this context - which I don't want to exist anyway.
        // I only need a strong reference to a fully @Inject-ed bean!
        context.release();
    }
}

MyBean is a @Dependent scoped bean with no @PreDestroy method that only needs to be garbage collected when I've finished with it. However, I can't find a lot of information about Providers, and so can't tell if I'm arming some kind of time-bomb by doing this.

Some of my @Dependent scoped beans (which I still obtain via Provider.get(), btw) are created by @Produces methods. Are they still in danger of being leaked?

Can anyone advise me, please?
Thanks,
Chris

Answer

Yuri picture Yuri · Jul 18, 2014

From Weld docs on lifecycle of @Dependent beans:

An instance of a dependent bean is never shared between different clients or different injection points. It is strictly a dependent object of some other object. It is instantiated when the object it belongs to is created, and destroyed when the object it belongs to is destroyed.

So injection of @Dependent object won't introduce a leak on its own, there's simply nothing to fix. Creating a short-lived context just "to be on the safe side" is totally unnecessary because dependent beans aren't tied to a context. As far as CDI is concerned after being injected they are ordinary (strongly reachable) Java objects.
If you need instantiation logic put it in a producer method and that's it.