当 bean 类型参数是类型变量时,CDI 类型安全解析在 weld 2.2.6 中不起作用

CDI typesafe resolution is not working in weld 2.2.6 when the the bean type parameter is a type variable

以下代码片段集适用于 "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 规范:

A parameterized bean type is considered assignable to a parameterized required type if they have identical raw type and for each parameter:

  1. the required type parameter and the bean type parameter are actual types with identical raw type, and, if the type is parameterized, the bean type parameter is assignable to the required type parameter according to these rules, or
  2. the required type parameter is a wildcard, the bean type parameter is an actual type and the actual type is assignable to the upper bound, if any, of the wildcard and assignable from the lower bound, if any, of the wildcard, or
  3. the required type parameter is a wildcard, the bean type parameter is a type variable and the upper bound of the type variable is assignable to or assignable from the upper bound, if any, of the wildcard and assignable from the lower bound, if any, of the wildcard, or
  4. the required type parameter is an actual type, the bean type parameter is a type variable and the actual type is assignable to the upper bound, if any, of the type variable, or
  5. the required type parameter and the bean type parameter are both type variables and the upper bound of the required type parameter is assignable to the upper bound, if any, of the bean type parameter.

那么,根据第 4 项,上面的代码应该可以工作,不是吗?

编辑 1: <-- 请参阅之前的 编辑 2。在那里我注意到 CDI 实现正在做我期望它应该做的事情,但我错误地认为它没有。

Antonin Stefanutti 的回答是正确的:上述规范的第 4 项不适用。但是,如果在解决 inyectión 点时知道类型变量 T 的实际类型,则可以获得 Spi.

的正确实例

Supouse Client class 是抽象的,一个实现指定了 class 定义中类型参数的类型。在这种情况下,可以发现实际类型并且应用规范的第 4 项。

如何解决?通过内省,使用Class#getGenericInterfaces()操作,对Client实例的实际Class。像这样:

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不能为我们做这个工作呢?通过内省,当具体 class 指定扩展超 class 的类型参数的实际类型时,可以发现封闭 class/interface 定义中声明的每个类型参数的实际类型。

编辑 2 对不起,忘了我说的一切。 CDI 确实在第二个示例中注入了 Client 的正确实例,而不需要我添加的程序初始化。它不会在我说的地方抛出 "WELD-001408: Unsatisfied dependencies for type Spi"。我错了。再次抱歉。 我应该删除这个问题吗?

在您的示例中,必需的类型参数 是类型变量,T,而不是实际类型,因此第 4 项不适用。实际上,在您的示例中满足了参数化 bean 类型到参数化所需类型的可分配性规范中提到的任何条件,这最终意味着不满足依赖关系。

这在某种程度上与 CDI-517 相关,虽然不完全相同,但澄清可以解释 Weld 1.x 和 Weld 2.x 之间的行为变化,因为规范已得到澄清。

如该讨论中所述,从 Java 语言的角度来看,Spi<Integer> 可分配 Spi<T> 是不正确的。