pep*_*uch 9 jsf dependency-injection java-ee cdi jsf-2
我是Java EE/JSF的新手,现在阅读有关CDI限定符的内容 - 更改类实现的可能性.这很棒,但我有一个问题.据我所知,我可以使用限定符更改类实现,但我需要在使用此实现的任何地方更改它.在一个地方做这件事的最佳解决方案是什么?凭借我对Java EE的小知识,我发现了这一点.
让我们假设我们正在创建简单的Calculator应用程序.我们需要创建几个类:
Calculator (计算器的基本实现)ScientificCalculator (科学实施计算器)MiniCalculator (潜力最小)MockCalculator (用于单元测试)@Calculator(将指示计算器的实际实现;我应该为每个实现创建限定符吗?)这是个问题.我有四个计算器的实现,我想在少数地方使用其中一个,但只有一个在时间(在我将使用的初始项目阶段MiniCalculator,然后Calculator等等).如何在没有更改代码的情况下在注入对象的每个位置更改实现?我应该创建负责注射的工厂method injector吗?我的解决方案是正确有意义的吗
厂
@ApplicationScoped
public class CalculatorFctory implements Serializable {
private Calculator calc;
@Produces @Calculator Calculator getCalculator() {
return new Calculator();
}
}
Run Code Online (Sandbox Code Playgroud)
使用计算器的类
public class CalculateUserAge {
@Calculator
@Inject
private Calculator calc;
}
Run Code Online (Sandbox Code Playgroud)
这是正确的解决方案吗?如果我错了或者有更好的解决方案,请纠正我.谢谢!.
rdc*_*rng 15
这里有几个问题.
@Alternatives.@PostConstruct.您还可以使用它来检查注入点并做出运行时决定注入的内容.请参阅链接2.了解一些线索.这个解决方案是否正确 这将有效,但你仍然需要弄乱代码来改变实现,所以首先考虑1. 也@Calculator Calculator似乎非常多余.再次,请参阅2处的链接.
@ApplicationScoped
public class CalculatorFctory implements Serializable {
private Calculator calc;
@Produces @Calculator Calculator getCalculator() {
return new Calculator();
}
}
Run Code Online (Sandbox Code Playgroud)更新:
除了用于依赖性解析的类型之外,CDI还使用限定符.换句话说,只要只有一种类型与注入点的类型匹配,单独的类型就足够了,不需要限定符.当单独的类型不够时,限定词就可以消除歧义.
例如:
public class ImplOne implements MyInterface {
...
}
public class ImplTwo implements MyInterface {
...
}
Run Code Online (Sandbox Code Playgroud)
为了能够注入任一实现,您不需要任何限定符:
@Inject ImplOne bean;
Run Code Online (Sandbox Code Playgroud)
要么
@Inject ImplTwo bean;
Run Code Online (Sandbox Code Playgroud)
这就是我说@Calculator Calculator多余的原因.如果为每个实现定义一个限定符,那么你获得的数据并不多,也可以只使用该类型.说,两个限定符@QualOne和@QualTwo:
@Inject @QualOne ImplOne bean;
Run Code Online (Sandbox Code Playgroud)
和
@Inject @QualTwo ImplTwo bean;
Run Code Online (Sandbox Code Playgroud)
上面的示例没有获得任何结果,因为在前面的示例中不存在任何歧义.
当然,您可以在无法访问特定实现类型的情况下执行此操作:
@Inject @QualOne MyInterface bean; // to inject TypeOne
Run Code Online (Sandbox Code Playgroud)
和
@Inject @QualTwo MyInterface bean; // to inject TypeTwo
Run Code Online (Sandbox Code Playgroud)
但是当他希望计算机实现被CDI管理时,OP不应该使用@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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
和
public class SomeProducer {
@Produces
public MyInterface get() {
if (conditionOne()) {
return new ImplOne();
} else if (conditionTwo()) {
return new ImplTwo();
} else {
return new ImplThree;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后,在第一个例子,CDI会管理的生命周期(即@PostConstruct和@Inject的东西从生产者返回的支持),但在第二个它不会.
回到最初的问题 - 在不必修改源代码的情况下,在实现之间切换的最佳方法是什么?假设您希望更改适用于应用程序.
@Default
public class ImplOne implements MyInterface {
...
}
@Alternative
public class ImplTwo implements MyInterface {
...
}
@Alternative
public class ImplThree implements MyInterface {
...
}
Run Code Online (Sandbox Code Playgroud)
然后,任何对任何@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>
Run Code Online (Sandbox Code Playgroud)
指定,在这种情况下ImplTwo将被注入到处.
进一步更新
Java EE环境中确实存在不受CDI管理的事情,例如EJB和Web服务.
您如何将Web服务注入CDI托管bean?这很简单:
@WebServiceRef(lookup="java:app/service/PaymentService")
PaymentService paymentService;
Run Code Online (Sandbox Code Playgroud)
就是这样,您可以在CDI以外的地方管理付款服务.
但是,如果您不想在@WebServiceRef(lookup="java:app/service/PaymentService")任何需要的地方使用它,该怎么办?如果你只想按类型注入怎么办?然后你在某个地方这样做:
@Produces @WebServiceRef(lookup="java:app/service/PaymentService")
PaymentService paymentService;
Run Code Online (Sandbox Code Playgroud)
并且在需要引用该支付服务的任何CDI bean中,您可以简单地@Inject使用CDI,如下所示:
@Inject PaymentService paymentService;
Run Code Online (Sandbox Code Playgroud)
请注意,在定义生产者字段之前,PaymentService不能用于注入CDI方式.但它始终以旧方式提供.此外,在任何一种情况下,Web服务都不由CDI管理,但定义生产者字段只是使该Web服务引用可用于注入CDI方式.
Avi*_*ngh 12
如果您想使用工厂方法在代码中交换实现,那么您的工厂方法是管理bean而不是CDI,因此实际上没有必要@Calculator.
@ApplicationScoped
public class CalculatorFactory implements Serializable {
enum CalculatorType{MiniCaculator,ScientificCaculator,MockCalculator};
Calculator getCalculator(CalculatorType calctype) {
switch(calctype)
case MiniCaculator : return new MiniCalculator();
case ScientificCalculator : new ScientificCalculator();
case MockCalculator : new MockCalculator();
default:return null;
}
}
public class CalculatorScientificImpl {
private Calculator calc =
CalculatorFactory.getCaclulator(CaclutorType.ScientificCalculator);
doStuff(){}
}
public class CalculatorTest {
private Calculator calc =
CalculatorFactory.getCaclulator(CaclutorType.MockCalculator);
doStuff(){}
}
Run Code Online (Sandbox Code Playgroud)
但是, 如果您希望使用@PostConstruct等对Caclulator bean进行CDI管理以进行注射和生命周期管理,那么您可以使用以下方法之一.
方法1:
优点:您可以避免使用创建注释@Named("miniCalculator")
缺点:如果有一个名称的改变,从说编译器不会给这种方法的错误miniCalculator来xyzCalculator.
@Named("miniCalculator")
class MiniCalculator implements Calculator{ ... }
@ApplicationScoped
public class CalculatorFactory implements Serializable {
private calc;
@Inject
void setCalculator(@Named("miniCalculator") Caclulator calc) {
this.calc = calc;
}
}
Run Code Online (Sandbox Code Playgroud)
方法2:推荐(如果任何注射失败,编译器会跟踪注射)
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD})
public @interface MiniCalculator{
}
@ApplicationScoped
public class CalculatorFactory implements Serializable {
private calc;
@Inject
void setCalculator(@MiniCalculator calc) {
this.calc = calc;
}
}
Run Code Online (Sandbox Code Playgroud)
方法3: 如果您使用工厂方法生成对象.它的生命周期不会被CDI管理,但注入将使用@Inject正常工作.
@ApplicationScoped
public class CalculatorFactory implements Serializable {
private Calculator calc;
@Produces Calculator getCalculator() {
return new Calculator();
}
}
public class CalculateUserAge {
@Inject
private Calculator calc;
}
Run Code Online (Sandbox Code Playgroud)
所有这三种方法都可用于测试,比如你有一个名为CaculatorTest的类,
class ScientificCalculatorTest{
Caclulator scientificCalculator;
@Inject
private void setScientificCalculator(@ScientificCalculator calc) {
this.scientificCalculator = calc;
}
@Test
public void testScientificAddition(int a,int b){
scientificCalculator.add(a,b);
....
}
}
Run Code Online (Sandbox Code Playgroud)
如果你想在你的测试中使用模拟实现,那么做这样的事情,
class CalculatorTest{
Caclulator calc;
@PostConstruct
init() {
this.calc = createMockCaclulator();
}
@Test
public void testAddition(int a,int b){
calc.add(a,b);
.....
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
22179 次 |
| 最近记录: |