Jakarta ee CDI与@products的依赖关系不明确-为什么?

Jakarta ee CDI与@products的依赖关系不明确-为什么?,jakarta-ee,cdi,weld,Jakarta Ee,Cdi,Weld,我使用的代码如下: public Configuration { private boolean isBatmanCar = someMethod(...); @Produces public Car getCar(@New Car car) { if(isBatmanCar) { car.setName("BatmanCar"); } return car; } } public Ca

我使用的代码如下:

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(JavaEE6BTW)时,我得到

AmbiguousResolutionException:WELD-001318无法解析带限定符[@Any@Default](…)生产者方法[Car]与限定符[@Any@Default]之间的不明确依赖关系。

我知道,当我将
@Alternative
添加到汽车类中时,它会起作用,但我想知道这是否是正确的方法,为什么我必须这样做

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


我正在使用JavaEE6、CDI1.0、EJB3.1、Glassfish 3.2这个错误来自这样一个事实:您有两个bean类型
Car
,一个是类,另一个是生产者。您有两个明显的解决方案来解决歧义:

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

或者,如果您真的想拥有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
在这里是不必要的,因为您的
Car
bean没有作用域(因此是
@Dependent
作用域)@只有当生产者使用不依赖于
@的作用域注入bean时,New才有用。也就是说,如果您的
Car
类在范围
@Dependent

中,则此代码不是很有用。另一种可能是在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解决方案更有用。

使用@Alternative works,但仅当您希望能够通过beans.xml激活它时才应使用

抑制bean的默认构造函数也可以工作,但您将无法在@RequestScoped以外的其他范围内使用bean

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

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

@Any
public class Car {
}
...
@Produces
public Car getCar() {
    return new Car();
}
...
@Inject
Car car;
您必须记住的事项:

  • 所有bean和生产者总是隐式限定为@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的默认构造函数对于注入点来说不是有效的可能性,而生产者是有效的可能性。

@Alternative允许您为同一bean(类型)提出不同的版本,并通过配置文件中的配置激活它。它可以用于在开发/测试和生产中使用不同的ben,例如,仅在beans.xml文件中更改它。