CDI Ambiguous dependency with @Produces - why?

前端 未结 3 1723
自闭症患者
自闭症患者 2020-12-06 11:13

I am using code like below:

public Configuration {

    private boolean isBatmanCar = someMethod(...);

    @Produces
    public Car getCar(@New Car car) {
          


        
相关标签:
3条回答
  • 2020-12-06 11:35

    The error comes from the fact that you have 2 beans of type Car, one being the class, the other being the producer. You have 2 obvious solutions to resolve the ambiguity:

    First, you put the logic behind isBatmanCar field in the original class (in a constructor or a @PostConstruct method for instance) and remove your producer. That would left only one Car bean.

    Or if you really want to have 2 bean or can't avoid it you should create a qualifier for your produced bean:

     @Target({ TYPE, METHOD, PARAMETER, FIELD })
     @Retention(RUNTIME)
     @Documented
     @Qualifier
     public @interface BatmanChecked {
     }
    

    and use it on producer,

    @Produces
    @BatmanChecked
    public Car getCar(Car car) {...}
    

    to be able to inject the type of car

    @Inject
    Car stdCar;
    
    @Inject
    @BatmanChecked
    Car batCheckedCar;
    

    Qualifier is the natural option to resolve ambiguous injection. Using @Alternative also works but it's more a trick here than a good practice.

    Last remark: @New is not necessary here, since your Car bean has no scope (so is @Dependent scoped). @New is only useful when a producer inject a bean with a scope that is not @Dependent. That said, this code is not very useful if your Car class is in scope @Dependent.

    0 讨论(0)
  • 2020-12-06 11:38

    Using @Alternative works but should only be used if you want to be able to activate it through beans.xml.

    Suppressing the default constructor of your bean also works but you won't be able to use your bean in another scope than @RequestScoped.

    Using your own qualifier works but isn't very useful if you have only one implementation and just want to be able to instantiate your bean with a producer rather than with its constructor.

    The easiest way is to annotate your bean @Any :

    @Any
    public class Car {
    }
    ...
    @Produces
    public Car getCar() {
        return new Car();
    }
    ...
    @Inject
    Car car;
    

    Things that you have to keep in mind :

    • All beans and producers are always implicitly qualified @Any
    • Beans and producers without explicit qualifiers are implicitly qualified @Default
    • Beans and producers with explicit qualifiers are no longer implicitly qualified @Default
    • Injection points without explicit qualifiers are implicitly qualified @Default, but not @Any

    Regarding all this, the same code as above explicitly qualified looks like this :

    @Any
    public class Car {
    }
    ...
    @Produces
    @Any
    @Default
    public Car getCar() {
        return new Car();
    }
    ...
    @Inject
    @Default
    Car car;
    

    It becomes more obvious that the bean's default constructor isn't a valid possibility for the injection point and the producer is a valid possibility.

    0 讨论(0)
  • 2020-12-06 11:38

    Another possibility would be to create non default constructor in the Car class like this:

    public Car {
        private String name = "NormalCar";
    
        public Car(String name) {
            this.name = name;
        }
        ...
    }
    

    by removing default constructor, Car class can no longer be used to create instances used by injection.

    And change your producer method to

    @Produces
    public Car getCar() {
        if(isBatmanCar) {
            return new Car("BatmanCar");
        }
        return new Car("NormalCar");
    }
    

    then producer method would be the only way to create your Cars.

    This way can be used, when you know that you would always need customized instance and you dont need default constructor. But usually Antoine solution is more useful.

    0 讨论(0)
提交回复
热议问题