Jsf 如何在多个类实现中使用CDI限定符?

Jsf 如何在多个类实现中使用CDI限定符?,jsf,jakarta-ee,jsf-2,dependency-injection,cdi,Jsf,Jakarta Ee,Jsf 2,Dependency Injection,Cdi,我是JavaEE/JSF新手,现在了解CDI限定符——更改类实现的可能性。这很好,但我有一个问题。据我所知,我可以使用限定符更改类实现,但我需要在使用此实现的任何地方更改它。在一个地方做这件事的最佳解决方案是什么?凭借我对JavaEE的一点了解,我找到了这个 让我们想象一下,我们正在创建一个简单的计算器应用程序。我们需要创建几个类: 计算器(计算器的基本实现) 科学计算器(计算器的科学实现) 小型计算器(潜力最小) MockCalculator(用于单元测试) 限定符@Calculator(将指

我是JavaEE/JSF新手,现在了解CDI限定符——更改类实现的可能性。这很好,但我有一个问题。据我所知,我可以使用限定符更改类实现,但我需要在使用此实现的任何地方更改它。在一个地方做这件事的最佳解决方案是什么?凭借我对JavaEE的一点了解,我找到了这个

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

  • 计算器
    (计算器的基本实现)
  • 科学计算器
    (计算器的科学实现)
  • 小型计算器
    (潜力最小)
  • MockCalculator
    (用于单元测试)
  • 限定符
    @Calculator
    (将指示计算器的实际实现;我是否应该为每个实现创建限定符?)
  • 问题是。我有四个calculator的实现,我想在几个地方使用其中一个,但一次只使用一个(在项目的初始阶段,我将使用
    miniculator
    ,然后使用
    calculator
    等等)。在注入对象的每个地方,我如何在不更改代码的情况下更改实现?我是否应该创建一个工厂,负责注射并作为
    方法注入器
    ?我的解决方案正确且有意义吗

    工厂

    @ApplicationScoped
    public class CalculatorFctory implements Serializable {
        private Calculator calc;
    
        @Produces @Calculator Calculator getCalculator() {
            return new Calculator();
        }
    }
    
    使用计算器的类

    public class CalculateUserAge {
        @Calculator
        @Inject
        private Calculator calc;
    }
    

    这是正确的解决方案吗?如果我错了或者有更好的解决办法,请纠正我。谢谢

    这里有几个问题

  • 在整个应用程序中更改所需实现的最佳方法是什么?调查
  • 每个实现是否需要限定符?否,请参阅答案以获得详细的解释
  • 我应该使用生产者来决定注入哪个实现吗?可能是你想要的解决方案,但我怀疑。生产者通常用于执行构造函数/
    @PostConstruct
    中无法完成的某种初始化。您还可以使用它来检查注入点,并在运行时决定注入什么。参见链接2。寻找一些线索
  • 这个解决方案正确吗?这将起作用,但是您仍然需要与代码混淆来更改实现,所以请考虑1。第一。另外,
    @Calculator
    似乎是高度冗余的。同样,请参见第2页的链接

    @ApplicationScoped
    public class CalculatorFctory implements Serializable {
        private Calculator calc;
    
        @Produces @Calculator Calculator getCalculator() {
            return new Calculator();
        }
    }
    
  • 更新:

    CDI除了使用类型外,还使用限定符进行依赖项解析。换句话说,只要只有一种类型与注入点的类型匹配,那么仅使用类型就足够了,不需要限定符。当类型本身还不够时,有限定符来消除歧义

    例如:

    public class ImplOne implements MyInterface {
        ...
    }
    
    public class ImplTwo implements MyInterface {
        ...
    }
    
    为了能够注入任一实现,您不需要任何限定符:

    @Inject ImplOne bean;
    

    这就是为什么我说
    @Calculator
    是多余的。如果您为每个实现定义了一个限定符,那么您不会获得太多,最好只使用该类型。比如说,两个限定符
    @QualOne
    @QualTwo

    @Inject @QualOne ImplOne bean;
    

    上面的例子没有任何好处,因为在前面的例子中,不存在歧义

    当然,您可以在无法访问特定实现类型的情况下执行此操作:

    @Inject @QualOne MyInterface bean; // to inject TypeOne
    

    然而,当OP希望计算器实现由CDI管理时,他不应该使用@products

    @Avinash Singh-CDI管理
    @生成的
    以及它们返回的任何内容,只要调用该方法的是CDI。请你看看。这包括返回`@…作用域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;
            }
        }
    }
    

    然后,在第一个示例中,CDI将管理从生产者返回的内容的生命周期(即
    @PostConstruct
    @Inject
    支持),但在第二个示例中不会

    回到最初的问题——不必修改源代码就可以在实现之间切换的最佳方式是什么?假设您希望更改是应用程序范围内的

    @Default
    public class ImplOne implements MyInterface {
        ...
    }
    
    @Alternative
    public class ImplTwo implements MyInterface {
        ...
    }
    
    @Alternative
    public class ImplThree implements MyInterface {
        ...
    }
    
    然后,对于任何
    @injectmyinterface实例
    ,将注入
    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>
    
    就是这样,您将拥有对CDI外部管理的支付服务的有效引用

    但是,如果您不想在任何需要的地方使用完整的
    @webservicef(lookup=“java:app/service/PaymentService”)
    ,该怎么办?如果您只想按类型注入它呢?然后你在某个地方这样做:

    @Produces @WebServiceRef(lookup="java:app/service/PaymentService")
    PaymentService paymentService;
    
    在任何需要对该支付服务进行引用的CDIBean中,您只需使用CDI将其注入,如下所示:

    @Inject PaymentService paymentService;
    

    请注意,在定义producer字段之前,
    PaymentService
    将无法通过CDI方式注入。但它总是以旧的方式提供。也,在任何一种情况下,web服务都不是由CDI管理的,但是定义producer字段只是使web服务引用可以通过CDI方式注入。

    如果您想使用工厂方法交换代码中的实现,那么工厂方法是管理bean而不是CDI,因此实际上不需要
    @计算器

        @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(){}
    }
    
    但是如果您希望使用@PostConstruct等对Caclulator bean进行CDI管理以进行注射和生命周期管理,则可以使用以下方法之一

    方法1:

    优点:您可以避免使用
    @Named(“小型计算器”)

    缺点:如果名称从say
    miniculator
    更改为
    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;
        }
    }
    
    方法2:推荐
    @WebServiceRef(lookup="java:app/service/PaymentService")
    PaymentService paymentService;
    
    @Produces @WebServiceRef(lookup="java:app/service/PaymentService")
    PaymentService paymentService;
    
    @Inject PaymentService paymentService;
    
        @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(){}
    }
    
    @Named("miniCalculator")
    class MiniCalculator implements Calculator{ ... }
    
    @ApplicationScoped
    public class CalculatorFactory implements Serializable {
        private calc;
    
        @Inject 
        void setCalculator(@Named("miniCalculator") Caclulator calc) {
            this.calc = calc;
        }
    }
    
    @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;
        }
    }
    
    @ApplicationScoped
    public class CalculatorFactory implements Serializable {
        private Calculator calc;    
        @Produces Calculator getCalculator() {
            return new Calculator();
        }
    }    
    public class CalculateUserAge {
        @Inject
        private Calculator calc;
    }
    
    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);
              .....
            }
            }