如何在多个类实现中使用 CDI 限定符? [英] How to use CDI qualifiers with multiple class implementations?
问题描述
我是 Java EE/JSF 的新手,现在阅读 CDI 限定符 - 更改类实现的可能性.这很好,但我有一个问题.据我所知,我可以使用限定符更改类实现,但我需要在使用此实现的任何地方更改它.在一个地方完成的最佳解决方案是什么?凭借我对 Java EE 的一点了解,我想出了这个.
让我们想象一下我们正在创建一个简单的计算器应用程序.我们需要创建几个类:
Calculator
(计算器的基本实现)ScientificCalculator
(计算器的科学实现)MiniCalculator
(潜力最小)MockCalculator
(用于单元测试)- 限定符
@Calculator
(将指示计算器的实际实现;我应该为每个实现创建限定符吗?)
问题来了.我有四种计算器的实现,我想在少数地方使用其中一种,但一次只能使用一种(在初始项目阶段,我将使用 MiniCalculator
,然后是 Calculator
> 等等).如何在注入对象的每个地方更改实现而不更改代码?我应该创建工厂来负责注入并作为方法注入器
工作吗?我的解决方案是否正确且有意义?
工厂
@ApplicationScoped公共类 CalculatorFctory 实现了 Serializable {私人计算器计算;@Produces @Calculator 计算器 getCalculator() {返回新的计算器();}}
使用计算器的类
公共类CalculateUserAge {@计算器@注入私人计算器计算;}
这是正确的解决方案吗?如果我错了或者有更好的解决方案,请纠正我.谢谢!
如果您想使用工厂方法交换代码中的实现,那么您的工厂方法正在管理 bean 而不是 CDI,因此实际上不需要@Calculator
.
@ApplicationScoped公共类 CalculatorFactory 实现了 Serializable {enum CalculatorType{MiniCaculator,ScientificCaculator,MockCalculator};计算器 getCalculator(CalculatorType calctype) {开关(计算类型)case MiniCaculator : 返回新的 MiniCalculator();case ScientificCalculator : new ScientificCalculator();case MockCalculator : new MockCalculator();默认值:返回空值;}}公共类 CalculatorScientificImpl {私人计算器 calc =CalculatorFactory.getCaclulator(CaclutorType.ScientificCalculator);做东西(){}}公共类 CalculatorTest {私人计算器 calc =CalculatorFactory.getCaclulator(CaclutorType.MockCalculator);做东西(){}}
但是如果您希望您的 Caclulator bean 由 CDI 管理以使用 @PostConstruct 等进行注入和生命周期管理,那么您可以使用以下方法之一.>
方法一:
优点:您可以避免使用 @Named("miniCalculator")
缺点:如果名称从 miniCalculator
更改为 xyzCalculator
,编译器不会使用这种方法给出错误.
@Named("miniCalculator")类 MiniCalculator 实现了 Calculator{ ... }@ApplicationScoped公共类 CalculatorFactory 实现了 Serializable {私人计算;@注入void setCalculator(@Named("miniCalculator") 计算器计算) {this.calc = calc;}}
方法 2:推荐(如果任何注入失败,编译器会跟踪注入)
@Qualifier@保留(运行时间)@目标({字段,类型,方法})公共@interface 迷你计算器{}@ApplicationScoped公共类 CalculatorFactory 实现了 Serializable {私人计算;@注入void setCalculator(@MiniCalculator calc) {this.calc = calc;}}
方法 3: 如果您使用工厂方法来生成对象.它的生命周期不会由 CDI 管理,但使用 @Inject 注入可以正常工作.
@ApplicationScoped公共类 CalculatorFactory 实现了 Serializable {私人计算器计算;@Produces 计算器 getCalculator() {返回新的计算器();}}公共类计算用户年龄{@注入私人计算器计算;}
所有三种方法都适用于测试,假设您有一个名为 CaculatorTest 的类,
class ScientificCalculatorTest{计算器科学计算器;@注入私有无效 setScientificCalculator(@ScientificCalculator calc) {this.scientificCalculator = calc;}@测试public void testScientificAddition(int a,int b){科学计算器.add(a,b);....}}
如果您想在测试中使用模拟实现,请执行以下操作,
类 CalculatorTest{计算器计算;@PostConstruct在里面() {this.calc = createMockCaclulator();}@测试public void testAddition(int a,int b){calc.add(a,b);.....}}
I'm new in Java EE/JSF and now read about CDI qualifiers - the possibility to change class implementation. This is great but I have got one question. As far as I understand I can change class implementation using qualifier but I need to change it everywhere I use this implementation. What is the best solution to do it in one place? With my small knowledge about Java EE I figured out this one.
Lets imagine that we are creating simple Calculator application. We need to create few classes:
Calculator
(basic implementation of calculator)ScientificCalculator
(scientific implementation of calculator)MiniCalculator
(with minimum potentiality)MockCalculator
(for unit tests)- Qualifier
@Calculator
(will indicate to the actual implementation of calculator; should I create qualifier for each implementation?)
Here is the question. I've got four implementations of calculator and I want to use one of them in few places but only one at time (in the initial project phase I will use MiniCalculator
, then Calculator
and so on). How can I change implementation without change code in every place where object is injected? Should I create factory which will be responsible for injecting and will work as method injector
? Is my solution correct and meaningful?
Factory
@ApplicationScoped
public class CalculatorFctory implements Serializable {
private Calculator calc;
@Produces @Calculator Calculator getCalculator() {
return new Calculator();
}
}
Class which uses Calculator
public class CalculateUserAge {
@Calculator
@Inject
private Calculator calc;
}
Is this the correct solution? Please correct me if I'm wrong or if there is a better solution. Thanks!.
If you want to swap the implementation in your code using a factory method then your factory method is managing the beans and not CDI and so there is really no need for @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(){}
}
However if you want your Caclulator beans to be CDI managed for injections and life cycle management using @PostConstruct etc then you can use one of the below approaches.
Approach 1 :
Advantage :You can avoid creating annotation using @Named("miniCalculator")
Disadvantage : compiler will not give an error with this approach if there is a name change from say miniCalculator
to 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;
}
}
Approach 2 : Recommended (Compiler keeps track of injection if any injection fails)
@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;
}
}
Approach 3: If you are using a factory method to generate your object.Its lifecycle wont be managed be CDI but the Injection will work fine using @Inject .
@ApplicationScoped
public class CalculatorFactory implements Serializable {
private Calculator calc;
@Produces Calculator getCalculator() {
return new Calculator();
}
}
public class CalculateUserAge {
@Inject
private Calculator calc;
}
All three approaches will work for testing , say you have a class named 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);
....
}
}
if you want to use a mock implementation in your test then do something like this,
class CalculatorTest{
Caclulator calc;
@PostConstruct
init() {
this.calc = createMockCaclulator();
}
@Test
public void testAddition(int a,int b){
calc.add(a,b);
.....
}
}
这篇关于如何在多个类实现中使用 CDI 限定符?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!