如何将无接口、外部库 类 注入 JavaEE 应用程序?

How to Inject no interface, external library classes into JavaEE application?

我对 JavaEE 和依赖注入的概念还很陌生。不过我对它还是有一定了解的,虽然我不知道它的所有使用方法。

我的本地界面如下:

@Local
public interface MyInterfaceLocal {
    SomeType getMeSometype();
}

实现该接口的class是stateless 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();
    }
}

我希望能够为我正在使用的外部库做(类似的事情),因为这允许我:

  1. 更改当前正在使用的底层外部库,对我的代码库进行最小的更改。如果需要的话,可能会换成更好的。
  2. 当我想对 MyInterfaceImpl class.
  3. 进行单元测试时,轻松注入模拟

到目前为止我看过-

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

  2. 使用Context & Dependency Injection 容器进行注入(就像EJB 容器那样做。我知道它不一样)。研究了使用 Producers 的能力。尽管我了解 Producers 对 CDI 的作用,但我无法理解如何利用它?或者即使我走在正确的道路上?

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

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 来传递这些参数来生成我想要的对象。

ExternalLibraryClass1Qualifier.java

@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 {
    @Inject
    @ExternalLibraryClass1Qualifier(argument1 = "something", argument2 = 7)
    ExternalLibrary externalLib;

    public SomeType getMeSomeType() {
        //Some implementation details...
        return externalLib.externalLibMethod();
    }
}

感谢您的指导。

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

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 生产者是一种方法,唯一的问题是,您究竟想如何构建它们。

既然你说参数来自 属性 文件,我建议你反转方法并让生产者方法检查 属性 文件并提取值(或询问任何 "reader" 之前这样做并缓存它):

@Produces
private ExternalLibraryClass1 produceExternalLibraryInstance() {
    // read the properties from the file or from any cache you use
    String arg1 = PropertyReader.getarg1();
    Integer arg2 = PropertyReader.getArg2();
    // create object with those args
    return new ExternalLibraryClass1(arg1, arg2);
}

有了这个你显然不需要限定符然后可以简单地做 @Inject ExternalLibraryClass1.

请注意,当您创建注入 ExternalLibraryClass1 的对象时,将调用生产者 - 确保您可以尽快从 属性 文件中获取参数。 (当 produces 读取时应该不会有问题,如果你有一些缓存可能会更棘手)