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) я получаю
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
боб не имеет смысла (так @Dependent
область видимости). @New полезно только тогда, когда производитель внедряет компонент с областью, которая не @Dependent
, Тем не менее, этот код не очень полезен, если ваш Car
класс находится в области видимости @Dependent
,
Использование @Alternative работает, но его следует использовать только в том случае, если вы хотите активировать его с помощью beans.xml.
Подавление конструктора по умолчанию для вашего bean-компонента также работает, но вы не сможете использовать ваш bean-компонент в другой области видимости, чем @RequestScoped.
Использование вашего собственного квалификатора работает, но не очень полезно, если у вас есть только одна реализация и вы хотите иметь возможность создавать экземпляр вашего компонента с производителем, а не с его конструктором.
Самый простой способ - аннотировать ваш бин @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;
Становится все более очевидным, что конструктор бина по умолчанию не является допустимой возможностью для точки внедрения, а производитель является допустимой возможностью.
Другой возможностью было бы создать конструктор не по умолчанию в классе 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");
}
тогда метод продюсера будет единственным способом создать ваши автомобили.
Этот способ можно использовать, когда вы знаете, что вам всегда понадобится настроенный экземпляр, и вам не нужен конструктор по умолчанию. Но обычно раствор Антуана более полезен.