Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 678
Chris Rankin Avatar asked Jul 18 '14 10:07

Chris Rankin


People also ask

What is CDI bean?

But what is a CDI bean? A CDI bean is a POJO, plain old java object, that has been automatically instantiated by the CDI container, and is injected into all, and any qualifying injection points in the application. The CDI container initiates the bean discovery process during deployment.

What is CDI Java?

CDI (Contexts and Dependency Injection) is a standard dependency injection framework included in Java EE 6 and higher. It allows us to manage the lifecycle of stateful components via domain-specific lifecycle contexts and inject components (services) into client objects in a type-safe way.


2 Answers

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.

like image 92
Yuri Avatar answered Sep 27 '22 19:09

Yuri


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!

like image 40
Martin Andersson Avatar answered Sep 27 '22 19:09

Martin Andersson