我必须使用什么类型的字面量才能使 CDI 的 Instance::select 方法正常工作?

What type literal must I use to have CDI's Instance::select method work properly?

假设我有这样一个界面:

public interface Converter<T> { /*...*/ }

假设在 CDI 环境中我已经成功地完成了这个:

@Inject
@Any
private Instance<Converter<?>> converters;

("successfully" 我的意思是我可以执行以下操作并在输出中看到多个转换器,因此可以正确发现和提供 bean:

for (final Object o : converters) {
  System.out.println("*** converter: " + o);
}

…所以 bean 发现不是问题。)

现在假设给定 Integer.class,我想这样做:

final TypeLiteral<Converter<Integer>> typeLiteral = new TypeLiteral<Converter<Integer>>(){};
final Instance<Converter<Integer>> subInstance = converters.select(typeLiteral);
final Converter<Integer> converter = subInstance.get();

这很好用。

现在,在我的实际代码中,Integer.class 被传入,作为满足声明为 Class<T> 的参数的值,所以我真正拥有的是:

final TypeLiteral<Converter<T>> typeLiteral = new TypeLiteral<Converter<T>>(){};
final Instance<Converter<T>> subInstance = converters.select(typeLiteral);
final Converter<T> converter = subInstance.get(); // this does not work

get() 调用失败,堆栈跟踪以如下内容开头:

org.jboss.weld.exceptions.UnsatisfiedResolutionException: WELD-001334: Unsatisfied dependencies for type Converter<T> with qualifiers @Any 
    at org.jboss.weld.bean.builtin.InstanceImpl.get(InstanceImpl.java:105)

我必须做什么才能让这个选择成功?

我注意到的一件事是堆栈报告找不到 Converter<T>。这看起来很可疑:我原以为它会用 Converter<Integer> 来代替,因为 T "slot" 在运行时是 "filled" 和 Integer.class,尽管, 公平地说,我确实提供了 new TypeLiteral<Converter<T>>(){},而不是 new TypeLiteral<Converter<Integer>>(){}

无论如何,所有这些告诉我 TypeLiteral<T> 正在使用 T 作为要查找的类型,而不是实际值 "filling" T "slot",事实上,没有声明为 implements Converter<T> 的转换器,只有一个声明为 implements Converter<Integer> 的转换器,所以我担心我想在这里做的事情根本不可能。

看起来你的Converter接口还没有被CDI发现。您可以通过向接口添加注释范围或通过 beans.xml 配置文件发现它来实现。

这个 有更好的解释。

创建 TypeLiteral 以捕获通用参数只有在编译时已知这些参数时才有效,因此 new TypeLiteral<Converter<Integer>>(){}.

如果类型参数在编译时未知,则 TypeLiteral 无法捕获参数信息,因为该信息已因类型擦除而被删除。所以创建一个 new TypeLiteral<Converter<T>>(){} 实际上只是创建一个 new TypeLiteral<Converter<object>>(){}.

这意味着您的 select(typeLiteral) 将无法按预期工作,因为它将收到 Converter<object>.

的类型文字