更新时间:2023-12-03 23:40:10
如果您想使用工厂方法交换代码中的实现,那么您的工厂方法正在管理 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)@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);
.....
}
}