如何使用CDI限定符与多个类实现? [英] How to use CDI qualifiers with multiple class implementations?
问题描述
让我们想象一下,我们正在创建简单的Calculator应用程序。我们需要创建几个课程:
-
计算器
(计算器的基本实现) / li>
-
ScientificCalculator
(科学实现计算器) -
MiniCalculator
(最小潜力)
-
MockCalculator
(用于单元测试) - 限定符
@Calculator
(将表示计算器的实际实现;我应该为每个实现创建限定词吗?)
这是问题。我有四个计算器的实现,我想在几个地方使用其中一个,但只有一个在时间(在初始项目阶段,我将使用 MiniCalculator
,然后计算器
等等)。在每个注入对象的地方,如何更改实现,而无需更改代码?我应该创建将负责注射的工厂,并将作为方法注入器
工作?我的解决方案是否正确和有意义?
工厂
@ApplicationScoped
public class CalculatorFctory implements Serializable {
private Calculator calc;
@Produces @Calculator Calculator getCalculator(){
return new Calculator();
}
}
使用计算器的类
public class CalculateUserAge {
@Calculator
@Inject
私人计算器calc;
}
这是正确的解决方案吗?如果我错了,请更正我,如果有更好的解决方案。谢谢!
如果要使用工厂方法在代码中交换实现,那么您的工厂方法就是管理bean而不是CDI,所以真的不需要 @Calculator
。
@ApplicationScoped
public class CalculatorFactory实现Serializable {
枚举CalculatorType {MiniCaculator,ScientificCaculator,MockCalculator};
计算器getCalculator(CalculatorType calctype){
switch(calctype)
case MiniCaculator:返回新的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(){}
}
然而如果您希望您的Caclulator bean使用@PostConstruct等进行注射和生命周期管理的CDI管理,那么您可以使用以下方法之一。
方法1:
优势:您可以避免使用@Named(miniCaclulator)创建注释
Disadvantage :如果将miniCaclulator从xyzCaclulator发出名称更改,则该方法不会给出错误。
@Named(miniCaclulator)
class MiniCalculator实现Calculator {...}
@ApplicationScoped
public class CalculatorFactory implements Serializable {
private calc;
@Inject
void setCalculator(@Named(miniCaclulator)Caclulator calc){
this.calc = calc;
}
}
方法2:推荐 (如果任何注射失败,编译器会跟踪注入)
@Qualifier
@Retention (RUNTIME)
@Target({FIELD,TYPE,METHOD})
public @interface MiniCalculator {
}
@ApplicationScoped
public class CalculatorFctory implements Serializable {
private calc;
@Inject
void setCalculator(@MiniCalculator calc){
this.calc = calc;
}
}
方法3: em>如果您使用工厂方法来生成对象。它的生命周期不会被管理为CDI,但注入可以使用@Inject工作正常。
@ApplicationScoped
public class CalculatorFactory implements Serializable {
private Calculator calc;
@Produces Calculator getCalculator(){
return new Calculator();
}
}
public class CalculateUserAge {
@Inject
private Calculator calc;
}
所有这三种方法都可以用于测试,比如说你有一个名为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);
....
}
}
如果你想在您的测试中使用模拟实现,然后执行此操作,
class CalculatorTest {
Caclulator calc;
@PostConstruct
init(){
this.calc = createMockCaclulator();
}
@Test
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("miniCaclulator")
Disadvantage : complier will not give an error with this approach if there is a name change from say miniCaclulator to xyzCaclulator.
@Named("miniCaclulator")
class MiniCalculator implements Calculator{ ... }
@ApplicationScoped
public class CalculatorFactory implements Serializable {
private calc;
@Inject
void setCalculator(@Named("miniCaclulator") 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 CalculatorFctory 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屋!