jakarta ee-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添加到Car类中时,它会起作用,但我想知道这是否是正确的方法,为什么我必须这样做?

你能告诉我在这种情况下@Produces的正确用法是什么吗?

我使用的是Java EE 6、CDI 1.0、EJB 3.1、Glassfish 3.2

错误来自于有两个类型为Car的bean,一个是类,另一个是生产者。你有两个明显的解决方案来解决歧义:

首先,将逻辑放在原始类的isBatmanCar字段后面(例如,在构造函数或@PostConstruct方法中),并删除生产者。这将只剩下一个Car bean。

或者,如果你真的想要2个bean或者无法避免它,你应该为你生产的bean创建一个限定符:

 @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,因为您的Carbean没有作用域(@Dependent作用域也是如此)@只有当生产者注入一个作用域不是@Dependent的bean时,New才有用。也就是说,如果您的Car类在作用域@Dependent中,则此代码不是很有用。

使用@Alternative有效,但只有当您希望能够通过beans.xml.激活它时才应该使用

取消bean的默认构造函数也可以,但您将无法在@RequestScoped之外的其他作用域中使用bean。

使用您自己的限定符是有效的,但如果您只有一个实现,并且只想用生产者而不是构造函数实例化您的bean,那么就不是很有用了。

最简单的方法是注释你的bean@Any:

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

你必须记住的事情:

  • 所有的豆子和生产者总是隐含地合格@Any
  • 没有显式限定符的Bean和生产者是隐式限定的@Default
  • 具有显式限定符的Bean和生产者不再具有隐式限定符@Default
  • 没有显式限定符的注入点是隐式限定的@Default,而不是@Any

关于所有这些,上面明确限定的相同代码看起来是这样的:

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

更明显的是,bean的默认构造函数对于注入点来说不是有效的可能性,而生产者是有效的可能性。

另一种可能性是在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");
}

那么生产者的方法将是创造你的汽车的唯一途径。

当您知道您总是需要自定义实例而不需要默认构造函数时,可以使用这种方式。但通常Antoine解决方案更有用。

最新更新