web-dev-qa-db-ja.com

CDI @Producesとのあいまいな依存関係-なぜですか?

私は以下のようなコードを使用しています:

_public Configuration {

    private boolean isBatmanCar = someMethod(...);

    @Produces
    public Car getCar(@New Car car) {
        if(isBatmanCar) {
            car.setName("BatmanCar");
        }
        return car;
    }
}

public Car {
    private String name = "NormalCar";

    public void setName(String name) {
        this.name = name;
    }
}

public Demo {
    @Inject
    Car car;

    // rest of code
}
_

アプリケーションをGlassfish(Java EE 6 btw)にデプロイすると、

AmbiguousResolutionException: WELD-001318 Cannot resolve an ambiguous dependency between (...) Car with qualifiers [@Any @Default] (...) Producer Method [Car] with qualifiers [@Any @Default]

Carクラスに_@Alternative_を追加すると機能することはわかっていますが、これが適切な方法であるかどうか、なぜそれを行う必要があるのでしょうか。

そのような場合の@Producesの正しい使用法を教えてください。

Java EE 6、CDI 1.0、EJB 3.1、Glassfish3.2を使用しています

12
Dariusz Mydlarz

エラーは、タイプCarのBeanが2つあり、1つはクラスで、もう1つはプロデューサーであるという事実に起因します。あいまいさを解決するための2つの明白な解決策があります。

まず、元のクラスのisBatmanCarフィールドの背後にロジックを配置し(たとえば、コンストラクターまたは@PostConstructメソッド内)、プロデューサーを削除します。 CarBeanが1つだけ残ります。

または、本当に2つのBeanが必要な場合、またはそれを回避できない場合は、生成されたBeanの修飾子を作成する必要があります。

 @Target({ TYPE, METHOD, PARAMETER, FIELD })
 @Retention(RUNTIME)
 @Documented
 @Qualifier
 public @interface BatmanChecked {
 }

プロデューサーで使用し、

@Produces
@BatmanChecked
public Car getCar(Car car) {...}

車の種類を注入できるようにする

@Inject
Car stdCar;

@Inject
@BatmanChecked
Car batCheckedCar;

修飾子は、あいまいな注入を解決するための自然なオプションです。 @Alternativeの使用も機能しますが、ここでは優れた方法というよりもトリックです。

最後の注意:Car Beanにはスコープがないため、ここでは@Newは必要ありません(@Dependentスコープもあります)。 @Newは、プロデューサーが@Dependentではないスコープを持つBeanを注入する場合にのみ役立ちます。とはいえ、Carクラスがスコープ@Dependentにある場合、このコードはあまり役に立ちません。

13

@Alternativeの使用は機能しますが、beans.xmlを介してアクティブ化できるようにする場合にのみ使用する必要があります。

Beanのデフォルトのコンストラクターを抑制しても機能しますが、@ RequestScoped以外のスコープでBeanを使用することはできません。

独自の修飾子を使用することは機能しますが、実装が1つだけで、コンストラクターではなくプロデューサーを使用してBeanをインスタンス化できるようにしたい場合はあまり役に立ちません。

最も簡単な方法は、Beanに@Anyアノテーションを付けることです。

@Any
public class Car {
}
...
@Produces
public Car getCar() {
    return new Car();
}
...
@Inject
Car car;

あなたが心に留めておかなければならないこと:

  • すべてのBeanとプロデューサーは常に暗黙的に@Anyで修飾されます
  • 明示的な修飾子のないBeanとプロデューサーは暗黙的に@Defaultで修飾されます
  • 明示的な修飾子を持つBeanとプロデューサーは、@ Defaultで暗黙的に修飾されなくなりました
  • 明示的な修飾子のないインジェクションポイントは、@ Defaultで暗黙的に修飾されますが、@ Anyでは修飾されません

これらすべてに関して、明示的に修飾された上記と同じコードは次のようになります。

@Any
public class Car {
}
...
@Produces
@Any
@Default
public Car getCar() {
    return new Car();
}
...
@Inject
@Default
Car car;

Beanのデフォルトのコンストラクターはインジェクションポイントの有効な可能性ではなく、プロデューサーは有効な可能性であることがより明らかになります。

9
Nicolas Lepage

もう1つの可能性は、次のようにCarクラスにデフォルト以外のコンストラクターを作成することです。

public Car {
    private String name = "NormalCar";

    public Car(String name) {
        this.name = name;
    }
    ...
}

デフォルトのコンストラクターを削除すると、Carクラスを使用してインジェクションで使用されるインスタンスを作成できなくなります。

そして、プロデューサーメソッドをに変更します

@Produces
public Car getCar() {
    if(isBatmanCar) {
        return new Car("BatmanCar");
    }
    return new Car("NormalCar");
}

それならプロデューサーメソッドがあなたの車を作る唯一の方法でしょう。

この方法は、常にカスタマイズされたインスタンスが必要であり、デフォルトのコンストラクターが必要ないことがわかっている場合に使用できます。しかし、通常はアントワーヌ溶液の方が便利です。

5
Gas