这里有几个问题。
- 在整个应用程序中更改所需实现的最佳方法是什么?查看
@Alternatives。
- 每个实现都需要一个限定符吗?不,请参阅this 答案以获得冗长而详细的解释。
- 我应该使用生产者来决定注入哪个实现吗?可能是您想要的解决方案,但我对此表示怀疑。生产者通常用于执行某种无法在构造函数/
@PostConstruct 中完成的初始化。您还可以使用它来检查注入点并在运行时决定注入什么。有关一些线索,请参阅链接 2。
-
这个解决方案正确吗?这会起作用,但是您仍然必须弄乱代码才能更改实现,因此请首先考虑 1.。 @Calculator Calculator 也似乎非常多余。同样,请参见 2 处的链接。
@ApplicationScoped
public class CalculatorFctory implements Serializable {
private Calculator calc;
@Produces @Calculator Calculator getCalculator() {
return new Calculator();
}
}
更新:
CDI 使用限定符除了 到依赖解析的类型。换句话说,只要只有一种类型与注入点的类型匹配,单独的类型就足够了,不需要限定符。当仅使用类型还不够时,可以使用限定符来消除歧义。
例如:
public class ImplOne implements MyInterface {
...
}
public class ImplTwo implements MyInterface {
...
}
为了能够注入任一实现,您不需要任何限定符:
@Inject ImplOne bean;
或
@Inject ImplTwo bean;
这就是为什么我说@Calculator Calculator 是多余的。如果您为每个实现定义一个限定符,您不会获得太多,还不如只使用类型。比如说,两个限定符@QualOne 和@QualTwo:
@Inject @QualOne ImplOne bean;
和
@Inject @QualTwo ImplTwo bean;
上面的例子没有任何收获,因为在前面的例子中已经不存在消除歧义了。
当然,在您无权访问特定实现类型的情况下,您可以这样做:
@Inject @QualOne MyInterface bean; // to inject TypeOne
和
@Inject @QualTwo MyInterface bean; // to inject TypeTwo
但是,当 OP 希望 Calculator 实现由 CDI 管理时,他不应该使用 @Produces。
@Avinash Singh - CDI 管理 @Produces 以及它们返回的任何内容,只要调用该方法的是 CDI。请参阅this section of the spec。这包括返回 `@...Scoped beans,它将支持依赖注入、生命周期回调等。
我在这里忽略了一些细节,所以考虑以下两个:
public class SomeProducer {
@Inject ImplOne implOne;
@Inject ImplTwo implTwo;
@Inject ImplThree implThree;
@Produces
public MyInterface get() {
if (conditionOne()) {
return implOne;
} else if (conditionTwo()) {
return implTwo;
} else {
return implThree;
}
}
}
和
public class SomeProducer {
@Produces
public MyInterface get() {
if (conditionOne()) {
return new ImplOne();
} else if (conditionTwo()) {
return new ImplTwo();
} else {
return new ImplThree;
}
}
}
然后,在第一个示例中,CDI 将管理从生产者返回的内容的生命周期(即@PostConstruct 和@Inject 支持),但在第二个示例中它不会。
回到最初的问题 - 在不修改源代码的情况下切换实现的最佳方式是什么?假设您希望更改适用于整个应用程序。
@Default
public class ImplOne implements MyInterface {
...
}
@Alternative
public class ImplTwo implements MyInterface {
...
}
@Alternative
public class ImplThree implements MyInterface {
...
}
然后,任何@Inject MyInterface instance、ImplOne 都会被注入,除非
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<alternatives>
<class>ImplTwo</class>
</alternatives>
</beans>
被指定,在这种情况下ImplTwo将被注入到任何地方。
进一步更新
Java EE 环境中确实有一些东西不受 CDI 管理,例如 EJB 和 Web 服务。
如何将 Web 服务注入 CDI 托管 bean?真的很简单:
@WebServiceRef(lookup="java:app/service/PaymentService")
PaymentService paymentService;
就是这样,您将获得对在 CDI 外部管理的支付服务的有效引用。
但是,如果您不想在任何需要的地方使用完整的 @WebServiceRef(lookup="java:app/service/PaymentService") 怎么办?如果你只想按类型注入呢?然后你在某个地方这样做:
@Produces @WebServiceRef(lookup="java:app/service/PaymentService")
PaymentService paymentService;
在任何需要引用该支付服务的 CDI bean 中,您可以像这样使用 CDI 简单地 @Inject 它:
@Inject PaymentService paymentService;
请注意,在定义生产者字段之前,PaymentService 将无法通过 CDI 方式进行注入。但它始终以旧方式可用。此外,在任何一种情况下,Web 服务都不是由 CDI 管理,但定义生产者字段只是使该 Web 服务引用可用于注入 CDI 方式。