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>
):
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));
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.
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).
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.