在CDI容器中手动注册类

问题描述 投票:15回答:4

我有一组通过反射实例化的类,因此这些类不由CDI容器管理,并且上下文不进行注入。我的问题是,有没有办法在CDI上下文中注册这些类,所以这些类是由上下文管理的?

贝娄,我是如何创建课程的:

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

如何制作由CDI容器管理的myClass实例?

java cdi
4个回答
13
投票

如果您的类被容器注册为bean,则可以使用编程查找来轻松获取它们。

@Inject
@Any
Instance<Object> myBeans;

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

你有。


3
投票

让CDI管理你的类的最简单方法是使用生产者。

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;
  }
}

你的代码中的其他地方你可以使用这样的生产者:

@Inject
@FromReflection
Object myBean;

**编辑:添加InjectionPoint用法。 **

现在,您可以通过在其参数列表中注入InjectionPointin来增强您的生产者。然后,您可以使用注入点的元数据(即限定符)来动态查找您的类。

首先,您必须添加一个字段来存储@FromReflection限定符中的类名:

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

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

那么你在你的制作人中使用这个信息:

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;
  }

}

请注意,生成的bean必须在@Dependent范围内,在生成器参数中注入InjectionPoint时,这是一个约束。你现在可以像这样注入你的bean:

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

现在,如果您想在运行时决定要构建哪个类,则必须使用CDI编程查找功能,该功能允许您创建合成限定符。首先为限定符创建一个AnnotationLiteral,以便能够实例化一个新的限定符。

public class FromReflectionLiteral extends AnnotationLiteral<FromReflection> implements FromReflection {

    private String value;

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

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

然后你将使用Instance<> bean来请求你的最终bean。

public class ConsumingBean {

    @Inject
    @Any
    Instance<Object> myBeanInstance;

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

下一步将是使用便携式扩展...


3
投票

根据@AdrianMitev的评论,我最终编写了这个类,它返回一个托管CDI Bean的实例,给出了它的类名(elName)或类类型:

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;
    }
}

所以,如果你有这样一个类:

@Named
public class FooClass {
...
}

您可以使用以下方式获取托管CDI实例:

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

或使用其elName

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

或者您可以选择要使用的名称:

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

并使用:

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

1
投票

您可以通过不自己实例化bean(正如我在上一篇文章中指出的那样)让CDI知道您的实例,但让CDI实例化bean。这是一个示例代码:

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;

如果您确定只有一种特定类型的bean,则可以使用beans.iterator.next()

© www.soinside.com 2019 - 2024. All rights reserved.