以下代码片段与“weld-core-1.1.5.AS71.Final.jar”(JBoss 7.1.1使用的那个)一起使用,但不适用于“weld-core-impl-2.2”。 6.Final.jar“(wildfly 8.2使用的那个)。
public class Client<T> {
public interface Spi<T> {
T getSomething();
}
@Inject
private Spi<T> spi; // WELD-001408: Unsatisfied dependencies for type Spi<Object>
}
public class SpiImpl implements Client.Spi<Integer> {
@Override
public Integer getSomething() {
return 5;
}
}
为什么呢? CDI 1.2规范:
参数化bean类型被认为可分配给参数化 如果它们具有相同的原始类型和每个参数,则为必需类型:
- 所需的类型参数和bean类型参数是实际的 具有相同原始类型的类型,如果类型是参数化的, bean类型参数可分配给所需的类型参数 根据这些规则,或
- 必需的类型参数是通配符,bean类型参数是实际类型,实际类型可分配给上层 如果有的话,绑定(如果有的话)通配符并且可以从下限赋值 任何,通配符,或
- 所需的类型参数是通配符,bean类型参数是类型变量,类型变量的上限是 可转让或转让自上限(如果有的话) 通配符并可从通配符的下限(如果有)分配, 或
- 所需的类型参数是实际类型,bean类型参数是类型变量,实际类型可分配给 类型变量的上限(如果有),或
- 必需的类型参数和bean类型参数都是类型变量,并且所需类型参数的上限是 可分配给bean类型参数的上限(如果有)。
醇>
所以,根据第4项,上面的代码应该有效,不应该吗?
编辑1:&lt; - 请先查看编辑2 。在那里,我注意到CDI实现正在按照我的预期应该做,但我错误地认为它没有。
Antonin Stefanutti回答是对的:上述规范的第4项不适用。但是,如果在解决了inyectión点时已知类型变量T的实际类型,则可以获得Spi
的正确实例。
Supouse Client
类是抽象的,实现指定类定义中类型参数的类型。在这种情况下,可以发现实际类型,并且适用规范的第4项。
如何解决?通过对Class#getGenericInterfaces()
实例的实际Class
使用Client
操作进行内省。像这样:
public class Test {
@Inject
private ClientImpl clientImpl;
public abstract static class Client<T> {
public interface Spi<T> {
T getSomething();
}
// @Inject inject don't work because CDI doesn't require it even when the actual type of T can be discovered. So, instead, I initialized it programaticaly in the postConstruct()
private Spi<T> spi; // WELD-001408: Unsatisfied dependencies for type Spi<Object> // Not true, later, in "edit 2" I noteced it works fine. Forget all this. Sorry.
@Inject
private Instance<Spi<?>> spiFinder;
/**Initializes the spi instance variable programaticaly */
@PostConstruct
private void postConstruct(){
ParameterizedType clientType = (ParameterizedType)this.getClass().getGenericSuperclass();
Type clientTypeParamter = clientType.getActualTypeArguments()[0];
Class<T> clientParameterClass = (Class<T>)clientTypeParamter;
final Iterator<Spi<?>> iterator = spiFinder.iterator();
while (iterator.hasNext()) {
Spi<?> spiCandidate = iterator.next();
ParameterizedType spiCandidateType = (ParameterizedType)spiCandidate.getClass().getGenericInterfaces()[0];
Type spiCandidateTypeParameter = spiCandidateType.getActualTypeArguments()[0];
Class<?> spiCandidateParameterClass = (Class<?>)spiCandidateTypeParameter;
if( clientParameterClass.isAssignableFrom(spiCandidateParameterClass)) {
if( spi != null)
throw new AmbiguousResolutionException();
spi = (Spi<T>)spiCandidate;
}
}
if( spi == null)
throw new UnsatisfiedResolutionException();
}
}
@Dependent
public static class SpiImpl_1 implements Client.Spi<Integer> {
@Override
public Integer getSomething() {
return 5;
}
}
@Dependent
public static class SpiImpl_2 implements Client.Spi<Double> {
@Override
public Double getSomething() {
return 5.0;
}
}
@Dependent
public static class ClientImpl extends Client<Integer> {}
}
以上代码有效(SpiImpl_1
的实例被注入spi
实例变量),那么,为什么CDI不能为我们做这个工作呢?通过instrospection,当具体类指定扩展超类的类型参数的实际类型时,可以发现在封闭类/接口定义中声明的每个类型参数的实际类型。
编辑2
对不起,忘了我说过的一切。 CDI确实在第二个示例中注入了Client
的正确实例,而不需要我添加的编程初始化。它不会抛出“WELD-001408:类型Spi不满意的依赖关系”,我说它确实如此。我错了。再次抱歉。
我应该删除这个问题吗?
答案 0 :(得分:1)
在您的示例中,必需类型参数是一个类型变量T
,而不是实际类型,因此项目#4不适用。实际上,在您的示例中,满足了规范中提到的参数化bean类型可赋值为参数化所需类型的任何条件,这最终意味着不满足依赖性。
这与CDI-517有某种关系,虽然不完全相同,但澄清可以解释Weld 1.x和Weld 2.x之间的行为变化,因为规范已经澄清。
正如在该讨论中所提到的,从Java语言的角度来看,Spi<Integer>
可分配到Spi<T>
是不正确的。