java - 当 bean 类实现也扩展了某些参数化接口(interface)的参数化接口(interface)时,CDI bean 类型解析问题

标签 java cdi jboss-weld parameterized-types

鉴于我缺乏解释该问题的词汇,我通过一个示例来展示它,该示例重现了失败并有助于查找原因:

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
        }
    }
}

第二个 block 显示了 bean 类的 bean 类型集 BeanClass1BeanClass2 ,根据Weld 。在那里我们发现bean类型集包含类型BaseType<T>而不是BaseType<SomeType1>BaseType<SomeType2> .

所以,bean类型为BeanClass2对应间接接口(interface)BaseType<P>Weld记得有类型变量 T而不是实际的类型参数 SomeType2 。 因此,根据this specification的最后一点, BeanClass2被错误地认为可分配给 BaseType<SomeType1> .

这是期望的行为还是错误?有解决方法吗?它是否已修复在新的Weld中版本。

测试在 JBoss AS 7.1.1 上执行,该版本使用 Maven 工件 org.jboss.as:jboss-as-weld:7.1.1

编辑: 我认为这个问题的原因不是第一个答案所建议的类型删除(它已被删除),而是 Weld 中的错误。 。生成 bean 类型所需的所有信息在运行时都可用。 我猜这个错误是在 Weld 时通过反射的方式生成bean类的bean类型。类型变量解析应该是递归的,但显然不是。

我确信生成间接接口(interface)的 bean 类型所需的信息在运行时是可用的,因为我制作并测试了一种使用库来完成此操作的方法 - 几年前我为内存高效的 java 序列化器制作了 - 幸运的是有一个函数可以完全满足我们的需要:生成/获取 Type java 类的每个祖先的实例,递归解析类型变量。我尝试将涉及的方法放在这里,但在粘贴代码时遇到格式问题;事实上,图书馆很长而且文档很少,这给了我放弃的理由。

为了至少展示如何解析间接接口(interface)的类型变量的本质,我编写了以下代码。它仅适用于特定类,但可以通过一些努力进行推广:

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;
}

我希望这可以避免相信类型删除是导致此问题的原因。

最佳答案

我认为这是一个限制。

如果我的理解是正确的,那么您的目标是通过注入(inject)和/或生成泛型类型来使 CDI 与泛型一起工作。

我前段时间也做了同样的事情,发现这是Java的限制。由于Java使用type erasure实现泛型,CDI无法正确处理泛型注入(inject)。

最后,你发现你有BaseType<T> ,但由于此限制,CDI 只能注入(inject)具体类型,例如 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;

}

另请检查此 post由 WELD/CDI 规范负责人制定。

所以,参数化 bean 没有问题,问题是最终有一个像 BaseType<T> 这样的泛型类型。 。

如果您可以给他们一个具体的类型,这应该按照我提到的帖子中的建议工作。不理想,但是...

希望对您有所帮助。

关于java - 当 bean 类实现也扩展了某些参数化接口(interface)的参数化接口(interface)时,CDI bean 类型解析问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15582027/

相关文章:

java - Android Firestore,如何在一个文档中设置多个时间戳

java - 将文本写入 AbstractTableModel 单元格

servlets - Java EE 6 企业应用程序 : Warning "Unsatisfied dependency: no bean matches the injection point" when injecting a bean

java - CDI 生产商未获得资格认证

带有 jetty-maven-plugin 和 cdi 的 Maven 项目无法正常工作

eclipse - 在 Java EE 开发中快速周转的理想设置是什么?

java - JEE6 拦截器 - 提取方法变量

java - 如何防止 Java 中的 SocketInputStream.socketRead0 挂起?

events - 使用成员的限定符动态触发 CDI 事件

java - 在 Java 中解析算术表达式并从中构建树