CDI对@Produces的不明确依赖 - 为什么?

Dar*_*arz 12 java-ee cdi weld

我使用的代码如下:

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
}
Run Code Online (Sandbox Code Playgroud)

当我将应用程序部署到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到Car类时它会工作,但我想知道这是否是正确的方法,为什么我必须这样做?

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

我正在使用Java EE 6,CDI 1.0,EJB 3.1,Glassfish 3.2

Ant*_*and 13

错误来自这样一个事实:你有2个类型的bean Car,一个是类,另一个是生产者.您有2个明显的解决方案来解决歧义:

首先,将逻辑隐藏isBatmanCar在原始类中(例如在构造函数或@PostConstruct方法中)并删除生成器.这只会留下一个Car豆子.

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

 @Target({ TYPE, METHOD, PARAMETER, FIELD })
 @Retention(RUNTIME)
 @Documented
 @Qualifier
 public @interface BatmanChecked {
 }
Run Code Online (Sandbox Code Playgroud)

并在生产者上使用它,

@Produces
@BatmanChecked
public Car getCar(Car car) {...}
Run Code Online (Sandbox Code Playgroud)

能够注入这种类型的汽车

@Inject
Car stdCar;

@Inject
@BatmanChecked
Car batCheckedCar;
Run Code Online (Sandbox Code Playgroud)

限定符是解决模糊注入的自然选择.使用@Alternative也有效,但这比一个好的做法更像是一个技巧.

最后一点:@New这里没有必要,因为你的Carbean没有作用域(所以是@Dependent作用域).@New仅在生产者注入一个范围不是的bean时才有用@Dependent.也就是说,如果您的Car类在范围内,则此代码不是很有用@Dependent.


小智 9

使用@Alternative工作,但只有在您希望能够通过beans.xml激活它时才能使用.

抑制bean的默认构造函数也可以工作,但是你不能在@RequestScoped之外的另一个范围内使用你的bean.

使用您自己的限定符可以工作,但如果您只有一个实现并且只是希望能够使用生成器而不是使用其构造函数来实例化bean,则它不是很有用.

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

@Any
public class Car {
}
...
@Produces
public Car getCar() {
    return new Car();
}
...
@Inject
Car car;
Run Code Online (Sandbox Code Playgroud)

你必须记住的事情:

  • 所有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;
Run Code Online (Sandbox Code Playgroud)

更明显的是,bean的默认构造函数不是注入点的有效可能性,并且生成器是有效的可能性.


Gas*_*Gas 5

另一种可能性是在Car类中创建非默认构造函数,如下所示:

public Car {
    private String name = "NormalCar";

    public Car(String name) {
        this.name = name;
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

通过删除默认构造函数,Car类不能再用于创建注入使用的实例.

并将您的生产者方法更改为

@Produces
public Car getCar() {
    if(isBatmanCar) {
        return new Car("BatmanCar");
    }
    return new Car("NormalCar");
}
Run Code Online (Sandbox Code Playgroud)

那么生产者方法将是创建汽车的唯一方法.

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