处理不同可注入实现的最佳方法是什么?



所以我有一个抽象的Java类和一些实现(在我的例子中是5

(。它看起来像这样:

public abstract class AbstractAbst {
String someCommonString;
@Inject
Logger logger;
public AbstractAbst () {
}
public AbstractAbst (String someCommonString) {
this.someCommonString = someCommonString;
}
}
public class Abst1 extends AbstractAbst {
public Abst1() {
super();
}
public Abst1 (String someCommonString) {
super(someCommonString);
}
}
public class Abst2 extends AbstractAbst {
public Abst2 () {
super();
}
public Abst2 (String someCommonString) {
super(someCommonString);
}
}
// ... And 3 more

现在在我的课堂上,我想使用这些类,我这样做是这样的:

public class AbstUser {
@Inject
Abst1 abst1;
@Inject
Abst2 abst2;
// ... And 3 more
public AbstUser () {
}
public doSomething {
abst1.someCommonString = "test";
abst2.someCommonString = "test";
// ... And 3 more
switch(someDecision) {
case 1:
abst1.doSomethingInAbst1();
break;
case 2:
abst1.doSomethingInAbst1();
break;
// ... And 3 more
}
}
}

但这不可能是最好的方法。不幸的是,我对JavaEE很陌生,所以我想不出更好的方法。

我的问题:

  • 有没有某种方法可以注入构造函数参数,以便我可以 省略abst1.someCommonString = "test"部分,因为它实际上 体积更大,因为它不仅仅是一个参数。
  • 我可以以某种方式 注入抽象类并在运行时确定哪个 我想使用的实施?

通过将@Inject放在重载构造函数上,您可以将其定义为注入点并通过它解析参数:

public abstract class Abst {
String someCommonString;
@Inject
Logger logger;
public Abst () {
}
@Inject
public Abst (@Named("myString") String someCommonString) {
this.someCommonString = someCommonString;
}
}

public class AbstUser {
@Produces
@Named("myString")
public String getMyString() {
return "test";
}

在getMyString函数中,你返回应该放在使用@Named("myString"(位置的字符串

CDI 生产者可以帮助处理此用例:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Qualifier
@interface Value {
@Nonbinding String value() default "";
}
public class ValueProducer {
@Produces
@Value
@Default
public String produceValue(InjectionPoint ip) {
Value val = ip.getAnnotated().getAnnotation(Value.class);
// get the value somehow, for this example we return value provided with annotation
return val.value();
}
}
public class Abst1 extends AbstractAbst {
@Inject
public Abst1 (@Value("value1") String someCommonString) {
super(someCommonString);
}
}
public class Abst2 extends AbstractAbst {
@Inject
public Abst2 (@Value("value2") String someCommonString) {
super(someCommonString);
}
}

我将假设您尝试构建:

public class AbstUser {
@Inject
AbstractAbst abst;
public doSomething() {
abst.doSomethingInAbst();
}
}

其中AbstractAbst声明为您提供,尽管要调用抽象方法:

public abstract class AbstractAbst {
@Inject
@Named("myString")
String someCommonString;
@Inject
Logger logger;
public abstract void doSomethingInAbst();
...
}

并且您希望确定在运行时注入的实际子类型的AbstractAbst

这样做的方法是将javax.inject.Qualifier与生产者结合使用。

限定符用于消除当注入点的类型为超类型时具有的歧义。

为了便于讨论,假设您要计算要注入的具体AbstractAbst类型。这将是一个"计算的抽象Abst"。因此,我们为此创建了一个限定符:

@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Computed {
}

限定词名称通常是形容词。我希望你能想出一个更好的领域特定形容词,而不是"计算"。

现在我们需要生成一个ComputedAbstractAbst对象。生产者方法通常编写在工厂类中,但它们可以添加到任何类型的 Bean 中。我们将javax.enterprise.inject.Instance实例变量(或成员,字段 - 无论你喜欢怎么称呼它们(注入到AbstComputer对象中,以便我们可以在需要时访问新的完全实现的Abst1Abst2对象的实例。换句话说,它们自己的所有实例变量都已完全注入。请参阅 javax.inject.Provider.get(( -javax.inject.Providerjavax.enterprise.inject.Instance的超级接口。

...
import javax.enterprise.inject.Produces;

public class AbstComputer {
@Inject
Instance<Abst1> abst1Instance;
@Inject
Instance<Abst2 abst2Instance;
// and three more
@Produces
@Computed
AbstractAbst computeAbst() {
// arbitrary logic to determine which type of AbstractAbst to create.
if (useAbst1())
return abst1Instance.get();
else if (useAbst2())
return abst2Instance.get();
// and three more
...
}

/**
* This is here for completeness, but it could be anywhere.
* It is copied from @theMahaloRecords solution.
*/
@Produces
@Named("myString") 
String lookupMyString() {
return "test";
}
}

@theMahaloRecords提供了生成"someCommonString"值的解决方案,值得称赞。

请注意,许多开发人员更喜欢声明自定义Qualifier而不是为此目的使用@Named,因为它是完全类型安全的,并且没有拼写错误混淆您的危险,例如@Named("mystring")

最后,您需要在注入点包含限定符:

public class AbstUser {
@Inject
@Computed
AbstractAbst abst;
public doSomething {
abst.doSomethingInAbst();
}
}

相关内容

最新更新