How to programmatically inject a Java CDI managed bean into a local variable in a (static) method
Asked Answered
P

6

39

How can I programmatically inject a Java CDI 1.1+ managed bean into a local variable in a static method?

Partly answered 17/7, 2014 at 8:30 Comment(0)
P
61

To inject an instance of class C:

javax.enterprise.inject.spi.CDI.current().select(C.class).get()

This is available in CDI 1.1+

Partly answered 20/7, 2014 at 16:30 Comment(10)
If you use CDI.current().select, you'll get an Instance<T>, don't forget to destroy it after using it.Fatshan
XDR, Your solution is working for me. I was trying a different way but couldn't get it working: context.getApplication().evaluateExpressionGet(context, "#{beanName}", BeanName.class) shouldn't they both work?Parks
@punchingInAPepper: I'm unfamiliar with the method that you mentioned, so, unfortunately, I can't answer your question.Partly
@Partly No worries, just thought it wouldn't hurt to ask. After searching for hours, this was the only solution that worked, It would be nice if there was more info on why. Do you have links to resources that helped you figure this out?Parks
@punchingInAPepper: I forget where I learned this. It might have been from the weld documentation: docs.jboss.org/weld/reference/latest/en-US/htmlPartly
@Fatshan destroy? Can you explain how?Creel
@Fatshan maybe means null-ing it? Mostly you want to keep an instance of your backing bean (get() returned it) in your converter/validator for performance reasons (please look at the source code of CDI's methods). I usually do this with a static field in the converter/validator: private static MyFooBackingBean FOO_CONTROLLER; and later: if (null == FOO_CONTROLLER) { FOO_CONTROLLER = CDI.current().select(MyFooBackingBean.class).get(); } Maybe annotations are missing here.Plourde
@Creel Destroying a bean reference obtained from an injected Instance<T> is necessary to avoid memory leaks, please see weld.cdi-spec.org/news/2016/05/18/enhanced-instanceFatshan
@Partly Thanks for the solution does this work with cdi2.0 ? i'm getting weld not initializedBeleaguer
@Beleaguer I haven't used CDI 2.0, but I imagine it should be backwards compatible with 1.1. I don't know about your weld issue, and I haven't used CDI or Java EE in yearsPartly
P
18

Use for instance this utility class. You basically have to obtain instance of BeanManager and than grab the bean you want from it (imagine something like JNDI lookup).

Update

You could also use CDI utility class offered in CDI 1.1

SomeBean bean = CDI.current().select(SomeBean.class).get();

Update 2

In CDI 2.0 you have to use BeanManager class for obtaining bean instances programatically.

Pina answered 17/7, 2014 at 8:43 Comment(2)
Thanks for the suggestion. I actually found a simpler solution, which I'll detail in an answering post.Partly
The CDI class does this nicely in one easy step.Faille
P
12

@BRS

import javax.enterprise.inject.spi.CDI;

...

IObject iObject = CDI.current().select(IObject.class, new NamedAnnotation("iObject")).get();

With:

import javax.enterprise.util.AnnotationLiteral;

public class NamedAnnotation extends AnnotationLiteral<Named> implements Named {

     private final String value;

     public NamedAnnotation(final String value) {
         this.value = value;
     }

     public String value() {
        return value;
    }
}
Partly answered 4/1, 2015 at 20:37 Comment(4)
So, after writing an implementation class for IObject with Named annotation value iObject, we need to write an anonymous inner class to get an instance. Isn't this a bit more complicated than by simply look up and cast? Or please correct me if my understanding is wrong.Johnsson
Probably it would be the better if you can actually make it more readable by having the import and dividing one step into two steps. That way it becomes more readable.Johnsson
NamedAnnotation can be used for any @Named bean, not just IObjects. ProgrammaticBeanLookup takes more code than NamedAnnotation, but can only be used for Class or @Named String, whereas the CDI route can take in other annotations and/or a TypeLiteral. ProgrammaticBeanLookup also requires JNDI classes to be in the classpath, and manual population of a non-final static variable if it isn't. Moreover, BeanManager isn't guaranteed to be threadsafe, so ProgrammaticBeanLookup might have threading issues.Partly
You can always wrap the CDI usage in a helper method, such as <T> T getNamedBean(Class<T> clazz, String name)Partly
J
3

The link suggested by @Petr Mensik is very useful. I am using the same code in my example.

Here is a way to get an instance of the class in instance methods/static methods. It is always better to code for interfaces instead of using the class name hard coded in the methods.

@Named(value = "iObject ")
@RequestScoped
class IObjectImpl  implements IObject  {.....}

//And in your method

IObject iObject = (IObject) ProgrammaticBeanLookup.lookup("iObject");
.........
//Invoke methods defined in the interface

This programmatic look up of beans can be quite useful when you have an application scoped object with method that requires an instance of a class that may change over time. So, it is always better to extract the interface and use programmatic bean look up for the sake of loose coupling.

Johnsson answered 3/1, 2015 at 16:41 Comment(1)
Please see my new answer that shows how annotations can be used with the CDI methodology, which provides more versatile and typesafe lookups than ProgrammaticBeanLookupPartly
C
2

You should include qualifiers:

List<Annotation> qualifierList = new ArrayList();
 for (Annotation annotation: C.class.getAnnotations()) {
   if (annotation.annotationType().isAnnotationPresent(Qualifier.class)) {
     qualifierList.add(annotation);
   }
 }
javax.enterprise.inject.spi.CDI.current()
   .select(C.class, qualifierList.toArray(new Annotation[qualifierList.size()])
   .get()
Cheeseburger answered 24/8, 2016 at 0:41 Comment(0)
F
0
  • You could define a parameter with the type of the bean interface in your static method, and pass an appropriate implementation reference. That would make it more unit-testing friendly.
  • If you're using Apache Deltaspike, you can use BeanProvider#getContextualReference. It's easier than getting a javax.enterprise.inject.Instance, but, beware of dependent beans (see javadoc)!
Fatshan answered 12/5, 2015 at 14:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.