When is a @Dependent scoped CDI bean destroyed, if you obtain that bean via Provider.get()?
Solution 1
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.
Solution 2
I haven't tested your construct, but me personally, whenever I need to go lookup a bean programmatically, I prefer CDI.current().select().get()
. Using this construct, I can confirm that you will get a new @Dependent
bean for each lookup. It is not tied to the provider (the CDI container in this case).
See this and this. What we have here is an Arquillian test setup that deploy a Servlet to a "real" (or "remote" using Arquillian terminology) server and issues HTTP GET request to find out what beans are produced on the inside.
The outcome is that both GlassFish 4.1 and WildFly 8.2.0 will provide the client code with one new @Dependent bean on each lookup and if I have understood your question correctly, then that is exactly what you want. Hope it helps!
Chris Rankin
Updated on June 04, 2022Comments
-
Chris Rankin almost 2 years
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'sProvider<>
field is "leaked", because it still "belongs" to theProvider
. - I have seen no evidence (yet!) of
@Dependent
beans being leaked by similarProvider
s 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 toInstance<>
, presumably to address the memory leak in CDI 1.0. But what aboutProvider<>
- does that still leak in CDI 1.1? (And if it does, then how are you supposed to useProvider.get()
?)Basically, I have several
@ApplicationScoped
beans /@Singleton
EJBs that I@Inject
Provider
fields into, and I am trying to useProvider.get()
as factories for both@Dependent
and@RequestScoped
"helper" beans. I definitely do not want these beans to "belong" to theirProvider
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
Provider
s 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 aboutProvider
s, 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 viaProvider.get()
, btw) are created by@Produces
methods. Are they still in danger of being leaked?Can anyone advise me, please?
Thanks,
Chris - A
-
Chris Rankin almost 10 yearsThanks for the reply. My particular
@Dependent
beans are transient "worker" beans rather than object fields. For example, one of my EJBs has a function that does something like: void updateStuff() { Helper helper = helperProvider.get(); // use helper ... } This approach allows the helper to have assorted cDI goodness injected into it, such as anEntityManager
etc. However, I was unaware that I was leaking theHelper
object afterwards! -
Chris Rankin almost 10 yearsBTW, according to the CDI documentation: "An instance of a bean with scope @Dependent obtained by direct invocation of an Instance is a dependent object of the instance of Instance." So it's slightly more than an "ordinary strongly reachable Java object" - it's bound to its creator! It is this binding that I am trying to undo with my workaround code.
-
Yuri almost 10 yearsSimply put that portion of the doc says "No matter how and where you inject it
@Dependent
is a dependent object of the injection point". So unless an owner of the injection point is leaked it'll be fine. On the side note, delegating from an EJB bean to a CDI bean is a bit strange; usually it's the other way round. Wouldn't a stateless EJB be more suitable than a@dependent
in this case? -
Chris Rankin almost 10 yearsMy
Provider
is a field on an@Singleton
EJB, and so the injection point never goes out of scope. Hence the leak. And I'm delegating from EJB to a CDI bean because a@Dependent
CDI bean is just a POJO; I only need a light-weight, transient "helper" object. Besides, I've seen other memory leaks with JBoss 6.1.0 and stateless EJBs because JBoss seems to constantly allocate new ones without reusing the old ones unless you configure them with a "strict max pool".