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

тогда метод производителя будет единственным способом создания ваших автомобилей.

этот способ можно использовать, когда вы знаете, что вам всегда понадобится настроенный экземпляр и вам не нужно конструктор по умолчанию. Но обычно решение Антуана более полезно.