我试图根据其ParameterizedType选择一个bean。看起来谷歌Guice相关解决方案有很多答案,但Java EE 7可以实现吗?
public interface Handler<T> { ... }
@ApplicationScoped StringHandler implements Handler<String> { ... }
@ApplicationScoped IntegerHandler implements Handler<Integer> { ... }
然后在其他一些托管bean中:
@Inject private Instance<Handler<?>> handlers;
public <T> Handler<T> handlerFor( Class<T> handledClass )
{
// ???
}
所以这里是Instance中与类型相关的select方法:
<U extends T> Instance<U> select(TypeLiteral<U> subtype, Annotation... qualifiers);
TypeLiteral的javadoc只是说你可以做像
这样的事情 TypeLiteral<List<String>> stringListType = new TypeLiteral<List<String>>() {};
这非常无益,因为我在编译时并不知道这些类型。但是,我将Class对象作为类型参数参数。
看起来Guice可以通过为你构造TypeLiteral对象来处理这个问题。 Java EE 7中没有类似的工具吗?
答案 0 :(得分:0)
我不太清楚你的问题。
但也许你可以在运行时通过
获得泛型类型String typeName = handledClass.getClass().getTypeParameters()[0].getName();
然后为每个CDI处理程序创建一个通用注释,并根据类型进行切换:
Annotation qualifier = typeName.equalsIgnoreCase("String") ? new AnnotationLiteral<StrHandler>() {} : new AnnotationLiteral<IntHandler> (){};
最后在您的实例中执行以下操作:
return handlers.select(qualifier).get();
编辑:有关基于http://docs.jboss.org/weld/reference/latest/en-US/html_single/#_obtaining_a_contextual_instance_by_programmatic_lookup上的AnnotationLiteral生成限定符注释的详细信息
不要忘记在注射时使用@Any
@Inject @Any Instance<Handler<?>> instance;
避免默认注射。
答案 1 :(得分:0)
聚会迟到,但我遇到了类似的问题,想分享我的方法:
我没有使用 Instance.select
(它需要只能在编译时构造的 TypeLiteral),而是简单地遍历所有实例(使用流),然后使用反射检查每个实例是否“适合”:
@Inject
@Any
Instance<Handler<?>> instances;
//...
Handler<?> handler = this.instances.stream()
.filter(it -> this.fits(it, handledClass))
.findAny().orElse(null);
fits 方法的实现有点 - 笨拙 - 可以这么说(注意:根据 CDI 实现,它可能需要一些调整!)。此外,根据用例,如果处理程序还应该支持子类(相反,可以使用 Class.isAssignableFrom
代替),那么简单的相等性检查可能是不够的!
private boolean fits(Class<?> clazz, Class<?> target)
{
if (clazz == null)
return false;
Type[] genericInterfaces = clazz.getGenericInterfaces();
boolean match = Arrays.stream(genericInterfaces)
.filter(it -> it instanceof ParameterizedType)
.map(it -> (ParameterizedType) it)
.filter(it -> it.getRawType() == Handler.class)
.anyMatch(it -> ((Class<?>) it.getActualTypeArguments()[0]) == target);
if (match)
return true;
if (this.fits(clazz.getSuperclass(), target))
return true;
if (Arrays.stream(clazz.getInterfaces()).anyMatch(it -> this.fits(it, target)))
return true;
return false;
}
请注意,递归是必需的,因为 handler.getClass() 将返回 CDI 客户端代理的类,不幸的是,它没有以通用方式实现基类型的接口。此外,如果我们有以下构造,不幸的是 FooHandlerImpl.class.getGenericInterfaces()
不会返回 Handler<Foo>
。所以我们需要自己遍历层次结构:
public interface FooHandler extends Handler<Foo> { }
public class FooHandlerImpl implements FooHandler { }
缓存结果可能是个好主意。