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]
Я знаю, что, когда я добавить @Alternative
для класса автомобилей это будет работать, но мне интересно, правильно ли это делать, и почему я должен это делать?
можете ли вы сказать мне, что такое правильное использование @Produces в таком случае?
Я использую Java EE 6, CDI 1.0, EJB 3.1, Glassfish 3.2
3 ответов
ошибка возникает из-за того, что у вас есть 2 боба типа Car
, один класс, другой-продюсер. У вас есть 2 очевидных решения для устранения двусмысленности:
во-первых, вы ставите логику isBatmanCar
поле в исходном классе (в конструкторе или @PostConstruct
метод, например) и удалите своего производителя. Что бы остался только один Car
бобовые.
или если вы действительно хотите иметь 2 боба или не можете избежать этого, вы должны создать квалификатор для вашего произведенного фасоли:
@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
также работает, но это больше трюк, чем хорошая практика.
последнее замечание: @New
здесь не нужно, так как ваш Car
bean не имеет области (так что @Dependent
уровня). @New полезен только тогда, когда производитель вводит боб с область, которая не @Dependent
. Тем не менее, этот код не очень полезен, если ваш Car
- класс в рамках @Dependent
.
использование @Alternative работает, но должно использоваться только в том случае, если вы хотите активировать его через бобы.XML.
подавление конструктора по умолчанию вашего компонента также работает, но вы не сможете использовать свой компонент в другой области, чем @RequestScoped.
использование собственного классификатора работает, но не очень полезно, если у вас есть только одна реализация и вы просто хотите создать экземпляр своего компонента с помощью производителя, а не с его конструктором.
в самый простой способ-аннотировать ваш bean @Any:
@Any
public class Car {
}
...
@Produces
public Car getCar() {
return new Car();
}
...
@Inject
Car car;
вещи, которые вы должны иметь в виду :
- все бобы и производители всегда неявно квалифицируются @Any
- бобы и производители без явных квалификаторов неявно квалифицируются @Default
- бобы и производители с явными квалификаторами больше не являются неявно квалифицированными @Default
- точки впрыска без явных квалификаторов неявно квалифицируются @Default, но не @Any
Что касается всего этого, тот же код, что и выше, явно квалифицирован выглядит следующим образом:
@Any
public class Car {
}
...
@Produces
@Any
@Default
public Car getCar() {
return new Car();
}
...
@Inject
@Default
Car car;
становится более очевидным, что конструктор по умолчанию bean не является допустимой возможностью для точки инъекции, а производитель-допустимой возможностью.
другой возможностью было бы создать конструктор не по умолчанию в классе автомобиля, как это:
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");
}
тогда метод производителя будет единственным способом создания ваших автомобилей.
этот способ можно использовать, когда вы знаете, что вам всегда понадобится настроенный экземпляр и вам не нужно конструктор по умолчанию. Но обычно решение Антуана более полезно.