鉴于我缺乏解释问题的词汇,我通过一个再现失败并帮助找到原因的例子来展示它:
public interface BaseType<P> {}
public interface DerivedType<T> extends BaseType<T> {}
public interface SomeType1 {}
public interface SomeType2 {}
@Dependent
public static class BeanClass1 implements DerivedType<SomeType1> {}
@Dependent
public static class BeanClass2 implements DerivedType<SomeType2> {}
@ApplicationScoped
public static class Test implements Serializable {
@Inject
private BaseType<SomeType1> field; // if not commented throws following exception during deployment: org.jboss.weld.exceptions.DeploymentException: WELD-001409 Ambiguous dependencies for type [BaseType<SomeType1>] with qualifiers [@Default] at injection point [[field] @Inject private Test.field]. Possible dependencies [[Managed Bean [class BeanClass2] with qualifiers [@Any @Default], Managed Bean [class BeanClass1] with qualifiers [@Any @Default]]]
@Inject
private Instance<BaseType<SomeType1>> instance;
@Inject
private BeanManager bm;
@PostConstruct
private void postConstruct() {
// This block outputs two bean classes and it should be only one:
// beanClass: BeanClass1@11be5bab
// beanClass: BeanClass2@764a72e9
{
Iterator<BaseType<SomeType1>> iterator = instance.iterator();
while (iterator.hasNext()) {
System.out.println("beanClass: " + iterator.next().toString());
}
}
// And this block outputs:
//
// bean: Managed Bean [class BeanClass1] with qualifiers [@Any @Default]
// beanTypes:
// - class BeanClass1
// - DerivedType<SomeType1>
// - class java.lang.Object
// - BaseType<T>
//
// bean: Managed Bean [class BeanClass2] with qualifiers [@Any @Default]
// beanTypes:
// - DerivedType<SomeType2>
// - class java.lang.Object
// - class BeanClass2
// - BaseType<T>
{
Set<Bean<?>> beans = bm.getBeans(new ParameterizedTypeImpl(BaseType.class, new Type[] { SomeType1.class }, null));
for (Bean<?> bean : beans) {
System.out.println("\nbean: " + bean+"\nbeanTypes: ");
for (Type beanType : bean.getTypes()) {
System.out.println(" - " + beanType);
}
}
bm.resolve(beans); // if not commeted throws an AmbiguousResolutionException
}
}
}
根据BeanClass1
,第二个块显示了bean类BeanClass2
和Weld
的bean类型集。我们发现bean类型集包含类型BaseType<T>
而不是BaseType<SomeType1>
或BaseType<SomeType2>
。
因此,与BeanClass2
记住的间接接口BaseType<P>
对应的Weld
的bean类型具有类型变量T
,而不是实际的类型参数SomeType2
}。
因此,根据this specification的最后一点,BeanClass2
被错误地视为可分配给BaseType<SomeType1>
。
这是期望的行为还是错误?有解决方法吗?它是否已修复为新的Weld
版本。
测试是在JBoss AS 7.1.1上执行的,它使用了maven artifact org.jboss.as:jboss-as-weld:7.1.1
编辑:
我认为这个问题的原因不是类型擦除,因为第一个答案建议(它被删除),但Weld
中的错误。生成bean类型所需的所有信息在运行时都可用。
我想,当Weld
通过反射生成bean类的bean类型时,错误就是。类型变量解析应该是递归的,显然,它不是。
我确信在运行时期间可以获得生成间接接口的bean类型所需的信息,因为我制作并测试了一个使用库完成此操作的方法 - 几年前我为了一个内存高效的java序列化器而做了 - 幸运的是有一个完全符合我们需要的函数:为java类的每个祖先生成一个/获取Type
实例,递归地解析类型变量。我试图将所涉及的方法放在这里,但是在粘贴代码时我遇到了问题;而且图书馆很长而且记录不完整的事实让我有理由放弃。
至少要说明如何解决间接接口的类型变量的本质,我编写了以下代码。它仅适用于特定类,但可以通过一些努力进行推广:
public static Set<Type> getActualInterfacesOfBeanClass1() {
Set<Type> actualInterfaces = new HashSet<>();
ParameterizedType directInterface = (ParameterizedType) BeanClass1.class.getGenericInterfaces()[0]; // = DerivedType<SomeType1> ; assumes only one interface is extended
actualInterfaces.add(directInterface);
// because ParameterizedType doesn't have a method like Class.getGenericInterfaces(), we have to do it by hand
Type indirectInterface; // = BaseType<SomeType1>
{
Class<?> directInterfaceRaw = (Class<?>) directInterface.getRawType(); // = DerivedType<T>
Map<String,Type> resolutionMap = new HashMap<>(); // = { T -> SomeType1 }
for( int i=0; i<directInterfaceRaw.getTypeParameters().length;++i) {
resolutionMap.put(directInterfaceRaw.getTypeParameters()[i].getName(),directInterface.getActualTypeArguments()[i]);
}
ParameterizedType indirectInterfaceUnresolved = (ParameterizedType) directInterfaceRaw.getGenericInterfaces()[0]; // = BaseType<T> ; assumes only one interface is extended
ArrayList<Type> resolvedParameters = new ArrayList<>(); // = { SomeType1 }
for(Type param : indirectInterfaceUnresolved.getActualTypeArguments()) {
Type resolvedParam;
if( param instanceof TypeVariable) {
resolvedParam = resolutionMap.get(((TypeVariable<?>)param).getName());
} else {
resolvedParam = param;
}
resolvedParameters.add(resolvedParam);
}
indirectInterface = new ParameterizedTypeImpl(indirectInterfaceUnresolved.getRawType(), resolvedParameters.toArray(new Type[resolvedParameters.size()]),indirectInterfaceUnresolved.getOwnerType());
}
actualInterfaces.add(indirectInterface);
return actualInterfaces;
}
我希望这可以避免相信类型擦除是造成这个问题的原因。
答案 0 :(得分:1)
我认为这是一个限制。
如果我做对了,你的目标是通过注入和/或生成通用类型使CDI与泛型一起工作。
前段时间我做了同样的事情,发现这是Java的限制。由于Java使用type erasure实现泛型,因此CDI无法正确处理泛型注入。
最后,您发现自己有BaseType<T>
,但由于此限制,CDI只能注入具体类型,例如BaseType<SomeType1>
。
检查我的情况,我认为这有点简单,但原则相同:
<强>接口强>
public interface Persistable implements Serializable {
...
}
不起作用
@Named
@RequestScoped
public class MyBean<T extends Persistable> {
@Inject
private T model;
}
<强>作品强>
@Named
@RequestScoped
public class MyBean<T extends Persistable> {
@Inject
private Persistable model;
}
同时通过WELD / CDI规范线索查看此post。
所以,参数化bean没有问题,最终问题是像BaseType<T>
这样的泛型类型。
如果你能给他们一个具体的类型,这应该像我提到的帖子中所建议的那样工作。不理想,但是......
我希望它有所帮助。
答案 1 :(得分:1)
我发现解决此错误的最少侵入性的方法是,对于bean类C
的每个参数化bean类型,应该具有T<A>
但Weld
形式错误地更改具有类型变量的实际类型参数A
,创建一个扩展T<A>
的新空接口,并将此导航接口添加到C
的已实现接口列表以及间接扩展的任何其他bean类中T<A>
。
特别是,对于上面的代码,bean类BaseType<SomeType1>
的bean类型BeanClass1
被Weld
错误地更改为BaseType<T>
。因此,解决问题所需的试验界面将是:
public interface PilotInterface1 extends BaseType<SomeType1> {}
应用了变通方法的bean类将是:
@Dependent
public static class BeanClass1 implements DerivedType<SomeType1>, PilotInterface1 {}
我建议将所有试验接口放在一个单独的包或文件中,以便在修复错误时轻松删除它们。