Question

I have a group of classes which are instantiated by reflection, hence these are not managed by the CDI container, and no injections are made by the context. My question is, is there any way to register these classes in the CDI context, so the classes get managed by the context?

Bellow, is how I create the classes:

String clazz = "org.myorg.thisIsMyClass";
MyClass myClass = Class.forName(clazz).newInstance(); // myClass instance not managed by CDI

How do I make the instance of myClass managed by the CDI container?

Was it helpful?

Solution

If your classes were registered as bean by the container you can use programmatic lookup to get them easily.

@Inject
@Any
Instance<Object> myBeans;

public Object getMyBeanFromClassName(String className) throws Exception{    
    Class clazz = Class.forName(className);
    return myBeans.select(clazz).get();  
}

Et voilà.

OTHER TIPS

Following the comments of @AdrianMitev, I finally ended up writing this class which returns an instance of a Managed CDI Bean given its class name (elName) or class type:

public class GetInstance {
    public static Object of(String elName) {
       BeanManager bm = getBeanManager();
       Bean<?> bean = bm.resolve(bm.getBeans(elName));
       return bm.getReference(bean, bean.getBeanClass(), bm.createCreationalContext(bean));
    }

    @SuppressWarnings("unchecked")
    public static <T> T of(Class<T> clazz) {
        BeanManager bm = getBeanManager();
        Bean<?> bean = bm.resolve(bm.getBeans(clazz));
        return (T) bm.getReference(bean, bean.getBeanClass(), bm.createCreationalContext(bean));
    }

    private static BeanManager getBeanManager() {
        try {
            return (BeanManager) new InitialContext().lookup("java:comp/BeanManager");
        } catch (NamingException e) {
            e.printStackTrace();
        }
        return null;
    }
}

So, if you have a class like this one:

@Named
public class FooClass {
...
}

You can get an a managed CDI instance using:

FooClass fC = GetInstance.of(FooClass.class);

or using its elName

FooClass fC = (FooClass) GetInstance.of("fooClass");

or you can select the name to use:

@Named(value="CustomFooClassName")
public class FooClass {
...
}

And using:

FooClass fC = (FooClass) GetInstance.of("CustomFooClassName");

The easiest way to do let CDI to manage your class is to use a producer.

public class MyProducers {

  @Produces
  @RequestScoped //Could be any scope here
  @FromReflection //a qualifier to distinguish this Bean of type Object from others. Would be better to have a more specific bean type if all your class come from the same ancestor.
  public Object produceMyClass()
  {
    String clazz = "org.myorg.thisIsMyClass";
    Object myObject = Class.forName(clazz).newInstance();
    return myObject;
  }
}

Somewhere else in your code you can use this producer like this :

@Inject
@FromReflection
Object myBean;

** Edit : adding InjectionPoint usage. **

Now you can enhance your producer by injecting it's InjectionPointin its parameter list. You can then use the metadata of the injection point (i.e qualifier) to dynamically find your class.

First you have to add a field to store the class name in your @FromReflection qualifier :

@Qualifier
@Target({TYPE, METHOD, PARAMETER, FIELD})
@Retention(RUNTIME)
@Documented
public @interface FromReflection {

    @Nonbinding String value(); // classname will be store here 
}

then you use this info in your producer :

public class MyProducers {

  private String extractClassName(InjectionPoint ip) {
    for (Annotation annotation : ip.getQualifiers()) {
        if (annotation.annotationType().equals(FromReflection.class))
            return ((FromReflection) annotation).value();
    }
    throw new IllegalStateException("No @FromReflection on InjectionPoint");
  }

  @Produces
  @FromReflection
  public Object produceMyClass(InjectionPoint ip)
  {
    String clazzNanme = extractClassName(ip);
    Object myObject = Class.forName(clazz).newInstance();
    return myObject;
  }

}

Note that the produced bean has to be in @Dependent scope, it's a constraint when injecting InjectionPoint in the producer parameters. You can now inject your bean like that :

@Inject
@FromReflection("org.myorg.thisIsMyClass")
Object myBean;

Now if you want to decide at runtime which class you want to build, you'll have to use the CDI programmatic lookup feature which allow you to create synthetic qualifier. First create an AnnotationLiteral for your qualifier to be able to instantiate a a new qualifier.

public class FromReflectionLiteral extends AnnotationLiteral<FromReflection> implements FromReflection {

    private String value;

    public FromReflectionLiteral(String value) {
        this.value = value;
    }

    @Override
    public String value() {
        return value;
    }
}

Then you'll use the Instance<> bean to request for your final bean.

public class ConsumingBean {

    @Inject
    @Any
    Instance<Object> myBeanInstance;

    public Object getBeanFor(String className) {
     return myBeanInstance.select(new FromReflectionLiteral(className)).get();
    }
    ...
}

Next step would be to use a portable extension...

You can make CDI aware of your instance by not instantiating the bean yourself (as i pointed in your previous post) but to let CDI instantiate the bean. Here is a sample code for that:

InitialContext initialContext = new InitialContext();
BeanManager bm = (BeanManager) initialContext.lookup("java:comp/BeanManager");

//List all CDI Managed Beans and their EL-accessible name
Set<Bean<?>> beans = bm.getBeans(AbstractBean.class, new AnnotationLiteral<Any>() {});
List<Object> beanInstances = new ArrayList<Object>();

for (Bean bean : beans) {
    CreationalContext cc = bm.createCreationalContext(bean);
    //Instantiates bean if not already in-service (undesirable)
    Object beanInstance = bm.getReference(bean, bean.getBeanClass(), cc);
    beanInstances.add(beanInstance);
}

return beanInstances;

If you are sure that there is only one bean of the specific type you can use beans.iterator.next().

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top