这里有几个问题。
- 在整个应用程序中更改所需实现的最佳方法是什么?调查
@Alternatives
。
- 每个实现都需要一个限定符吗?不,请参阅此答案以获得冗长而详细的解释。
- 我应该使用生产者来决定注入哪个实现吗?可能是您想要的解决方案,但我对此表示怀疑。生产者通常用于执行某种无法在构造函数 / 中完成的初始化
@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。如果您愿意,请参阅规范的这一部分。这包括返回 `@...Scoped bean,它将支持依赖注入、生命周期回调等。
我在这里忽略了一些细节,所以考虑以下两个:
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 {
...
}
然后, any for any将被注入,@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 中,您都可以@Inject
像这样简单地使用 CDI:
@Inject PaymentService paymentService;
请注意,在定义生产者字段之前,PaymentService
将无法通过CDI 方式进行注入。但它总是可用的旧方式。此外,在任何一种情况下,Web 服务都不由 CDI 管理,但定义生产者字段只是使该 Web 服务引用可用于以 CDI 方式注入。