2

問題を説明するための語彙が不足しているため、失敗を再現し、原因を見つけるのに役立つ例を使用して説明します。

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

2 番目のブロックは、 に従って、Bean クラスBeanClass1およびの Bean タイプのセットを示しています。そこで、Bean タイプ セットにorの代わりにタイプが含まれていることがわかりました。BeanClass2WeldBaseType<T>BaseType<SomeType1>BaseType<SomeType2>

そのため、記憶BeanClass2する間接インターフェースに対応するBaseType<P>のBean タイプは、実際の型パラメーターではなく、型変数を持ちます。したがって、この仕様の最後の点によると、は誤って に割り当て可能と見なされます。WeldTSomeType2BeanClass2BaseType<SomeType1>

これは望ましい動作ですか、それともバグですか? 回避策はありますか? 新しいWeldバージョンで修正されましたか。

テストは、maven アーティファクト org.jboss.as:jboss-as-weld:7.1.1 を使用する JBoss AS 7.1.1 で実行されました。

編集:この問題の原因は、最初の回答が示唆するように型の消去ではなく(削除されました)、のバグだと思いますWeld。Bean タイプを生成するために必要なすべての情報は、実行時に利用できます。Weldバグは、リフレクションによって Bean クラスの Bean タイプを生成するときだと思います。型変数の解決は再帰的であるべきですが、明らかにそうではありません。

ライブラリを使用してこれを達成するメソッドを作成およびテストしたため、間接インターフェイスの Bean タイプを生成するために必要な情報が実行時に利用できると確信しています。つまりType、Java クラスの祖先ごとにインスタンスを生成/取得し、型変数を再帰的に解決します。関連するメソッドをここに書き込もうとしましたが、コードを貼り付けるときにフォーマットの問題がありました。そして、ライブラリが長く、文書化が不十分であるという事実が、私にあきらめる理由を与えました。

少なくとも、間接インターフェースの型変数を解決する方法の本質を示すために、次のコードを書きました。これは特定のクラスに対してのみ機能しますが、ある程度の努力で一般化できます。

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

これにより、型の消去がこの問題の原因であると信じられなくなることを願っています。

4

2 に答える 2

1

限界だと思います。

これが正しければ、あなたの目標は、ジェネリック型を注入および/または生成することにより、CDI をジェネリックと連携させることです。

しばらく前に同じことをしたところ、これが Java の制限であることがわかりました。Java はerase を使用してジェネリックを実装するため、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 仕様リーダーによるこの投稿も確認してください。

したがって、パラメータ化された Bean を使用しても問題ありません。問題は、最終的にはBaseType<T>.

それらに具体的なタイプを与えることができれば、これは私が言及した投稿で提案されているように機能するはずです。理想的ではありませんが...

お役に立てば幸いです。

于 2013-03-23T07:40:16.673 に答える
1

このバグを回避するために私が見つけた最も邪魔にならない方法Cは、フォームを持つ必要があるがT<A>、型変数でWeld実際の型パラメーターを誤って変更している Bean クラスのパラメーター化されたすべての Bean 型に対して、これを拡張して追加Aする新しい空のインターフェイスを作成することですT<A>の実装されたインターフェースリストへのパイロットインターフェース、Cおよび間接的に拡張する他の Bean クラスへのインターフェースT<A>

特に、上記のコードBaseType<SomeType1>では、Bean クラスのBean タイプBeanClass1が誤って to に変更されWeldていBaseType<T>ます。したがって、問題を回避するために必要なパイロット インターフェイスは次のようになります。

public interface PilotInterface1 extends BaseType<SomeType1> {}

回避策が適用された Bean クラスは次のようになります。

@Dependent
public static class BeanClass1 implements DerivedType<SomeType1>, PilotInterface1 {}

バグが修正されたときに簡単に削除できるように、すべてのパイロット インターフェイスを別のパッケージまたはファイルにまとめることをお勧めします。

于 2013-04-08T17:44:46.080 に答える