Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canonical way to obtain CDI managed bean instance: BeanManager#getReference() vs Context#get()

I figured that there are two general ways to obtain an auto-created CDI managed bean instance via BeanManager when having solely a Bean<T> to start with (which is created based on Class<T>):

  1. By BeanManager#getReference(), which is more often shown in snippets:

    Bean<TestBean> bean = (Bean<TestBean>) beanManager.resolve(beanManager.getBeans(TestBean.class));
    TestBean testBean1 = (TestBean) beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));
    
  2. By Context#get(), which is less often shown in snippets:

    Bean<TestBean> bean = (Bean<TestBean>) beanManager.resolve(beanManager.getBeans(TestBean.class));
    TestBean testBean2 = beanManager.getContext(bean.getScope()).get(bean, beanManager.createCreationalContext(bean));
    

In effects, they do ultimately exactly the same thing: returning a proxied reference to the current CDI managed bean instance and auto-creates the bean instance if it doesn't already exist in the scope.

But they do it a bit differently: the BeanManager#getReference() always creates a whole new proxy instance, while the Context#get() reuses an existing proxy instance if already created before. This is evident when the above code is executed in an action method of an existing TestBean instance:

System.out.println(testBean1 == testBean2); // false
System.out.println(testBean1 == this); // false
System.out.println(testBean2 == this); // true

The javadoc of Context#get() is very explicit in this:

Return an existing instance of certain contextual type or create a new instance by calling Contextual.create(CreationalContext) and return the new instance.

while the javadoc of BeanManager#getReference() is not explicit enough on this:

Obtains a contextual reference for a certain bean and a certain bean type of the bean.

This got me confused. When do you use the one or the other? For the both ways you need a Bean<T> instance anyway, from which the bean class and bean scope is readily available which is required as additional argument. I can't imagine why they would need to be supplied externally in this specific case.

I can imagine that Context#get() is more memory efficient as it doesn't unnecessarily create another proxy instance referring the very same underlying bean instance, but just finds and reuses an existing proxy instance.

This puts me to the following question: when exactly is the BeanManager#getReference() more useful than Context#get()? It's more often shown in snippets and more often recommended as solution, but it only unnecessarily creates a new proxy even when one already exists.

like image 676
BalusC Avatar asked Nov 18 '13 12:11

BalusC


2 Answers

beanManager#getReference gives you a new instance of a client proxy but the client proxy will forward method calls to the current contextual instance of a particular context. Once you obtain the proxy and keep it and the method calls will be invoked on the current instance (e.g. current request). It is also useful if the contextual instance is not serializable - the client proxy will be and will reconnect after you deserialize it.

BeanManager#getContext obtains the target instance without a client proxy. You may still see a Weld's proxy in the class name but that is an enhanced subclass that provides interception and decoration. If the bean is not intercepted nor decorated this will be a plain instance of the given bean.

Usually (1) is more suitable unless you have a special use-case where you need to access the target instance directly (e.g. to access its fields).

Or in other words

1) BeanManager#getReference will return a 'Contextual Reference', with a normal scoping proxy for the bean. If a bean have with @SessionScoped as

@SessionScoped User user;

Then the contextual reference user will 'point' to the respective User instance (the 'Contextual Instance') of the current session for each invocation. Two different invocations to user.getName() from two different web browsers will give you different answers.

2) Context#get() will return a the internal 'Contextual Instance' without the normal scoping proxy.This is usually nothing a user should call himself. If you get the User user for "Bob" that way and store it in an @ApplicationScoped bean or in a static variable, then it will always remain to be the user "Bob" - even for web requests from other browsers! You will get a direct, non-proxied instance.

like image 122
Asif Bhutto Avatar answered Sep 30 '22 05:09

Asif Bhutto


I have a Singleton that I was using the getReference() method to get the reference to. Even though the singleton was already initialized, the proxy created through getReference() called the @PostConstruct each time getReference() was used.

@Startup
@ApplicationScoped
@Singleton

@PostConstruct
private void initialize() {}

By switching to the getContext().get() method, the unnecessary @PostConstruct proxy calls are no longer made.

like image 33
tinman13 Avatar answered Sep 30 '22 05:09

tinman13