Jakarta ee 如何将没有接口的外部库类注入JavaEE应用程序?

Jakarta ee 如何将没有接口的外部库类注入JavaEE应用程序?,jakarta-ee,dependency-injection,mocking,cdi,Jakarta Ee,Dependency Injection,Mocking,Cdi,我对JavaEE和依赖注入的概念相当陌生。然而,我对它有一个公平的理解,即使我不知道它可以被使用的所有方式 我有一个本地接口,如下所示: @Local public interface MyInterfaceLocal { SomeType getMeSometype(); } 实现此接口的类是无状态EJB @Stateless public class MyInterfaceImpl { public SomeType getMeSomeType() { //

我对JavaEE和依赖注入的概念相当陌生。然而,我对它有一个公平的理解,即使我不知道它可以被使用的所有方式

我有一个本地接口,如下所示:

@Local
public interface MyInterfaceLocal {
    SomeType getMeSometype();
}
实现此接口的类是无状态EJB

@Stateless
public class MyInterfaceImpl {
    public SomeType getMeSomeType() {
        //Some implementation details...
        ExternalLibraryClass externalLib = new ExternalLibrary(arg1, arg2);
        return externalLib.externalLibMethod();
    }
}
现在的问题是,我如何避免实例化
externalLib
,并让它以某种方式注入?例如,如果这是我用接口创建的另一个EJB,那么我可以简单地让EJB容器处理实例化,并使用
@EJB
注释,如下所示

@Stateless
public class MyInterfaceImpl {
    @EJB
    AnotherInterface anotherInterfaceImpl;

    public SomeOtherType getMeSomeType() {
        //Some implementation details...

        return anotherInterfaceImpl.someMethod();
    }
}
@Stateless
public class MyInterfaceImpl {
    @Inject
    @ExternalLibraryClass1Qualifier(argument1 = "something", argument2 = 7)
    ExternalLibrary externalLib;

    public SomeType getMeSomeType() {
        //Some implementation details...
        return externalLib.externalLibMethod();
    }
}
我希望能够对我正在使用的外部库执行(类似的操作),因为这允许我:

  • 更改当前正在使用的基础外部库,对我的代码库进行最小的更改。如果需要,可以换一个更好的
  • 当我想对
    MyInterfaceImpl
    类进行单元测试时,可以轻松地插入一个mock
  • 到目前为止,我已经看过-

  • 创建一个包装器方法,其参数是
    外部库
    ,因此可以执行某种手动方法参数注入。这仍然使我的实现与底层库紧密耦合。(或者我做得不对)

  • 使用
    Context&Dependency Injection
    container来进行注入(就像EJB容器那样。我知道这不一样)。研究了使用
    生产者的能力。虽然我了解
    制作人
    在CDI方面做了什么,但我不知道如何利用这一点?或者即使我走对了路

  • 更新: 我找到了一些帮助我更好地理解CDI制作人的文章,我尝试采用这种方法,但遇到了另一个问题。所以现在我有:

    ExternalLibraryProducer.java

    public class ExternalLibraryProducer {
        @Produces
        private ExternalLibraryClass1 extrnalLibraryClassProducer() {
            return new ExternalLibraryClass1("SomeString", 7);
            //The constructor actually takes a string and another commplex type 
            //as parameters. I am keeping it a little simple here.
            //I am trying to set the ExternalLibraryClass1() arguments 
            //programmatically at runtime.
        }
    }
    
    @Qualifier
    @Retention(RUNTIME)
    @Target({METHOD})
    public @interface ExternalLibraryClass1Qualifier {
        String argument1();
        Int argyment2(); //This is actually another complex type. Keeping it 
        //simple here.
    }
    
    现在,我要生成的对象的构造函数接受参数,比如字符串和整数。我想我可以创建一个
    限定符
    来传入这些参数以生成我想要的对象

    externallibraryClassQualifier.java

    public class ExternalLibraryProducer {
        @Produces
        private ExternalLibraryClass1 extrnalLibraryClassProducer() {
            return new ExternalLibraryClass1("SomeString", 7);
            //The constructor actually takes a string and another commplex type 
            //as parameters. I am keeping it a little simple here.
            //I am trying to set the ExternalLibraryClass1() arguments 
            //programmatically at runtime.
        }
    }
    
    @Qualifier
    @Retention(RUNTIME)
    @Target({METHOD})
    public @interface ExternalLibraryClass1Qualifier {
        String argument1();
        Int argyment2(); //This is actually another complex type. Keeping it 
        //simple here.
    }
    
    现在我想做的是,我想在运行时以编程方式设置参数值(假设,从属性文件)。我不知道该怎么做。因此,我的最后注射将如下所示

    @Stateless
    public class MyInterfaceImpl {
        @EJB
        AnotherInterface anotherInterfaceImpl;
    
        public SomeOtherType getMeSomeType() {
            //Some implementation details...
    
            return anotherInterfaceImpl.someMethod();
        }
    }
    
    @Stateless
    public class MyInterfaceImpl {
        @Inject
        @ExternalLibraryClass1Qualifier(argument1 = "something", argument2 = 7)
        ExternalLibrary externalLib;
    
        public SomeType getMeSomeType() {
            //Some implementation details...
            return externalLib.externalLibMethod();
        }
    }
    

    感谢您的指导。

    您可以让
    外部库的producer方法接受创建对象所需的参数,并且可以通过CDI producerr生成这些参数

    public class ExternalLibraryProducer {
    
        private String name;
    
        private int age;
    
        public ExternalLibraryProducer() {
            // set name and age from properties file or elsewhere
            // or you can set them individually on their respective producer methods, but might be costly considering you need to read the properties file twice
        }
    
        @Produces
        public String name() {
            return name;
        }
    
        @Produces
        public int age() {
            return age;
        }
    
        ....
    
        @Produces
        private ExternalLibraryClass1 extrnalLibraryClassProducer(String name, int age) {
            // Or you can use them directly here, if you opt to not separate this config from the dependencies
            return new ExternalLibraryClass1(argument, age);
        }
    }
    

    您可以让
    ExternalLibrary
    的producer方法接受创建对象所需的参数,并且可以通过CDI producerr生成这些参数

    public class ExternalLibraryProducer {
    
        private String name;
    
        private int age;
    
        public ExternalLibraryProducer() {
            // set name and age from properties file or elsewhere
            // or you can set them individually on their respective producer methods, but might be costly considering you need to read the properties file twice
        }
    
        @Produces
        public String name() {
            return name;
        }
    
        @Produces
        public int age() {
            return age;
        }
    
        ....
    
        @Produces
        private ExternalLibraryClass1 extrnalLibraryClassProducer(String name, int age) {
            // Or you can use them directly here, if you opt to not separate this config from the dependencies
            return new ExternalLibraryClass1(argument, age);
        }
    }
    

    CDI制作人是一种方式,唯一的问题是,你到底想如何构建他们

    既然您说参数来自属性文件,我建议您反转方法,让producer方法检查该属性文件并提取值(或者询问之前执行此操作并缓存它的任何“阅读器”):

    有了它,您显然不需要限定符,只需执行
    @injectexternallibraryclass1


    只需注意,当您创建一个注入
    外部libraryClass1
    的对象时,将根据调用producer-请确保您很快就能从属性文件中获取args。(当products进行读取时应该不会有问题,如果您有一些缓存,可能会更棘手)

    CDI products是一种方法,唯一的问题是,您希望如何准确地构建它们

    既然您说参数来自属性文件,我建议您反转方法,让producer方法检查该属性文件并提取值(或者询问之前执行此操作并缓存它的任何“阅读器”):

    有了它,您显然不需要限定符,只需执行
    @injectexternallibraryclass1


    只需注意,当您创建一个注入
    外部libraryClass1
    的对象时,将根据调用producer-请确保您很快就能从属性文件中获取args。(当PRODUCTS进行读取时应该不会有问题,如果您有一些缓存,可能会更棘手)

    CDI PRODUCTOR看起来是个不错的方法,尽管这里的限定符参数不起作用-我认为注入点与PRODUCTOR方法不匹配。也许可以反转方法,将
    argument1
    argument2
    存储在制作者在创建对象时可以查看的地方?CDI制作者看起来是个不错的方法,尽管限定符参数在这里不起作用-注入点与我认为的制作者方法不匹配。也许可以颠倒方法,将
    argument1
    argument2
    存储在制作人创建对象时可以查看的地方?