Java 在CDI容器中手动注册类
我有一组由反射实例化的类,因此这些类不由CDI容器管理,也不由上下文进行注入。我的问题是,有没有办法在CDI上下文中注册这些类,以便这些类由上下文管理 下面是我创建类的方式:Java 在CDI容器中手动注册类,java,cdi,Java,Cdi,我有一组由反射实例化的类,因此这些类不由CDI容器管理,也不由上下文进行注入。我的问题是,有没有办法在CDI上下文中注册这些类,以便这些类由上下文管理 下面是我创建类的方式: String clazz = "org.myorg.thisIsMyClass"; MyClass myClass = Class.forName(clazz).newInstance(); // myClass instance not managed by CDI 如何让CDI容器管理myClass的实例?您可以让C
String clazz = "org.myorg.thisIsMyClass";
MyClass myClass = Class.forName(clazz).newInstance(); // myClass instance not managed by CDI
如何让CDI容器管理
myClass
的实例?您可以让CDI知道您的实例,而不是自己实例化bean(正如我在上一篇文章中指出的那样),而是让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;
InitialContext InitialContext=new InitialContext();
BeanManager bm=(BeanManager)initialContext.lookup(“java:comp/BeanManager”);
//列出所有CDI托管bean及其EL可访问名称
Set让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
来增强生产者。然后,您可以使用注入点(即限定符)的元数据来动态查找您的类
首先,您必须添加一个字段以在@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
范围内,当在producer参数中注入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;
}
}
公共类FromReflectionLiteral扩展了AnnotationLiteral实现FromReflection{
私有字符串值;
public FromReflectionLiteral(字符串值){
这个值=值;
}
@凌驾
公共字符串值(){
返回值;
}
}
然后,您将使用实例bean来请求您的最终bean
public class ConsumingBean {
@Inject
@Any
Instance<Object> myBeanInstance;
public Object getBeanFor(String className) {
return myBeanInstance.select(new FromReflectionLiteral(className)).get();
}
...
}
公共类消费豆{
@注入
@任何
实例myBeanInstance;
公共对象GetBean(字符串类名称){
返回myBeanInstance.select(newfromReflectionLiteral(className)).get();
}
...
}
下一步是使用一个可移植的扩展…在@AdrianMitev的注释之后,我最终编写了这个类,该类根据其类名(elName)或类类型返回托管CDI Bean的实例:
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;
}
}
您可以使用以下方法获取托管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,那么您可以使用编程查找来轻松获取它们
@Inject
@Any
Instance<Object> myBeans;
public Object getMyBeanFromClassName(String className) throws Exception{
Class clazz = Class.forName(className);
return myBeans.select(clazz).get();
}
@Inject
@任何
实例myBeans;
公共对象getMyBeanFromClassName(字符串className)引发异常{
Class clazz=Class.forName(className);
返回myBeans.select(clazz.get();
}
Et voilá。您的方法很有趣,尽管您可能知道,类的名称并不是一个常量,我使用了一个常量作为说明;使用反射是因为要创建的类是未知的,可以是任何确定类型的类,因此它是可变的。那么,您的方法如何将参数接受到@producer
方法中呢?通过创建另一个@producer
?您可以通过使用InjectionPoint
(我修改了我的答案以向您展示如何操作),缺点是您生成的bean必须在@依赖范围内。您必须使用一个包含bean来给它一个作用域。也可能有基于可移植扩展的解决方案,但我真的需要更多地了解您的用例。这是您的好代码,尽管我最终使用了另一种方法,但您的方法也是有效的,事实上,要找到有关注释文字
和生产者的动态参数的示例并不容易,就像你的那个。我在这篇文章的答案中发布了我的方法,你可以看到我最终实现了什么。我的代码回答了最初的问题。你的没有。只有当您的类被CDI容器发现并注册为bean类时,它才起作用,因为CDI不允许在运行时创建新的bean。如果您的bean能够有效地管理这段代码,那么我可以告诉您,看到这段代码会让我的CDI规范变得冷淡。它可以工作,但完全超出了CDI的精神……严格地说,是的,这是针对CDI已经发现的类。整个想法是在不使用@Inject
的情况下获取由CDI管理的类的实例。假设您有50个类,并且在运行时您知道要使用什么类,那么将所有50个类注入到一个类中并使用一个类是没有意义的。由CDI发现一个类的事实并不保证您将获得一个托管实例,而且在不使用@Inject
的情况下获得由CDI管理的实例绝非易事。因此,不要关注类是否被发现,问题是如何管理实例。很抱歉,这个解决方案是错误做法的汇编:当您可以注入BeanManager时,从JNDI检索BeanManager。当您有更高级别的工具(如编程查找)时,直接使用bean管理器;当您可以从bean的类型请求bean时,使用慢速EL解析;当CDI基于强类型时,对名称进行解析,并且仅为UI提供名称解析。我发布了一个新的答案,用更少的行和CDI良好的实践来做同样的事情。好吧,你可以把我的答案吐得到处都是,但是你忘记了最重要的部分。不,如果没有注意到创建的实例,则不能插入BeanManager
@Inject
@Any
Instance<Object> myBeans;
public Object getMyBeanFromClassName(String className) throws Exception{
Class clazz = Class.forName(className);
return myBeans.select(clazz).get();
}