私は以下のようなコードを使用しています:
_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を使用しています
エラーは、タイプCar
のBeanが2つあり、1つはクラスで、もう1つはプロデューサーであるという事実に起因します。あいまいさを解決するための2つの明白な解決策があります。
まず、元のクラスのisBatmanCar
フィールドの背後にロジックを配置し(たとえば、コンストラクターまたは@PostConstruct
メソッド内)、プロデューサーを削除します。 Car
Beanが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
にある場合、このコードはあまり役に立ちません。
@Alternativeの使用は機能しますが、beans.xmlを介してアクティブ化できるようにする場合にのみ使用する必要があります。
Beanのデフォルトのコンストラクターを抑制しても機能しますが、@ RequestScoped以外のスコープでBeanを使用することはできません。
独自の修飾子を使用することは機能しますが、実装が1つだけで、コンストラクターではなくプロデューサーを使用してBeanをインスタンス化できるようにしたい場合はあまり役に立ちません。
最も簡単な方法は、Beanに@Anyアノテーションを付けることです。
@Any
public class Car {
}
...
@Produces
public Car getCar() {
return new Car();
}
...
@Inject
Car car;
あなたが心に留めておかなければならないこと:
これらすべてに関して、明示的に修飾された上記と同じコードは次のようになります。
@Any
public class Car {
}
...
@Produces
@Any
@Default
public Car getCar() {
return new Car();
}
...
@Inject
@Default
Car car;
Beanのデフォルトのコンストラクターはインジェクションポイントの有効な可能性ではなく、プロデューサーは有効な可能性であることがより明らかになります。
もう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");
}
それならプロデューサーメソッドがあなたの車を作る唯一の方法でしょう。
この方法は、常にカスタマイズされたインスタンスが必要であり、デフォルトのコンストラクターが必要ないことがわかっている場合に使用できます。しかし、通常はアントワーヌ溶液の方が便利です。