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

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

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

Другие вопросы по тегам