我有一组通过反射实例化的类,因此这些类不由CDI容器管理,并且上下文不进行注入。我的问题是,有没有办法在CDI上下文中注册这些类,所以这些类是由上下文管理的?
贝娄,我是如何创建课程的:
String clazz = "org.myorg.thisIsMyClass";
MyClass myClass = Class.forName(clazz).newInstance(); // myClass instance not managed by CDI
如何制作由CDI容器管理的myClass
实例?
如果您的类被容器注册为bean,则可以使用编程查找来轻松获取它们。
@Inject
@Any
Instance<Object> myBeans;
public Object getMyBeanFromClassName(String className) throws Exception{
Class clazz = Class.forName(className);
return myBeans.select(clazz).get();
}
你有。
让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
用法。 **
现在,您可以通过在其参数列表中注入InjectionPoint
in来增强您的生产者。然后,您可以使用注入点的元数据(即限定符)来动态查找您的类。
首先,您必须添加一个字段来存储@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();
}
...
}
下一步将是使用便携式扩展...
根据@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");
您可以通过不自己实例化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()
。