如何在多个类实现中使用 CDI 限定符? [英] How to use CDI qualifiers with multiple class implementations?

查看:14
本文介绍了如何在多个类实现中使用 CDI 限定符?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 Java EE/JSF 的新手,现在阅读 CDI 限定符 - 更改类实现的可能性.这很好,但我有一个问题.据我所知,我可以使用限定符更改类实现,但我需要在使用此实现的任何地方更改它.在一个地方完成的最佳解决方案是什么?凭借我对 Java EE 的一点了解,我想出了这个.

让我们想象一下我们正在创建一个简单的计算器应用程序.我们需要创建几个类:

  1. Calculator(计算器的基本实现)
  2. ScientificCalculator(计算器的科学实现)
  3. MiniCalculator(潜力最小)
  4. MockCalculator(用于单元测试)
  5. 限定符 @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:

  1. Calculator (basic implementation of calculator)
  2. ScientificCalculator (scientific implementation of calculator)
  3. MiniCalculator (with minimum potentiality)
  4. MockCalculator (for unit tests)
  5. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆