基于类型参数的Java EE注入

时间:2014-11-14 15:24:01

标签: java java-ee generics cdi java-ee-7

我试图根据其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中没有类似的工具吗?

2 个答案:

答案 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 {  }

缓存结果可能是个好主意。