【问题标题】:Inject service depending on configuration in Java EE根据 Java EE 中的配置注入服务
【发布时间】:2026-01-26 03:10:01
【问题描述】:

我的应用程序当前使用一个类,通过@Inject 注释将两个服务注入其中。

@Stateless
public class MyClass {

  @Inject
  private SomeService someService;

  @Inject
  private OtherService otherService;
}

这两个服务非常相似,都扩展了一个抽象的Service 类。

这就是我想要做的......

我的基本想法是 MyClass 类看起来像这样:

@Stateless
public class MyClass {

  @Inject
  private Service service;
}

根据配置,应用程序决定注入SomeServiceOtherService

示例:

if (config.getValue().equals("some_service")) {
  return new SomeService();
} else if (config.getValue().equals("other_service")) {
  return new OtherService();
}

Jave EE 是否为此提供了解决方案?

【问题讨论】:

  • 您是否尝试将“if-then-else-if-then”代码放入 @Producer 带注释的方法中?应该只是工作......

标签: java jakarta-ee cdi java-ee-7


【解决方案1】:

要完成这项工作,您需要确保任何“制造”SomeService 从它可以制造的类型列表中消除 Service,并且无论“制造”OtherService 从列表中消除Service it 可以制作的类型。

例如,如果 SomeService 是一个简单的托管 bean,则需要向其添加 @Typed(SomeService.class) 注释:

@Typed(SomeService.class)
public class SomeService extends Service {

}

另一方面,如果SomeService 是由生产者方法生成的,则您必须类似地执行相同的操作:

@Produces
@ApplicationScoped
@Typed(SomeService.class)
private SomeService makeSomeService() {
  return fabricateSomeService();
}

@Typed 注释将类型集限制为给定的任何内容,而不是推断的内容。

如果您在两个“具体”服务上都这样做,那么您在上面的答案中写的 getService() producer 方法应该可以工作。

【讨论】:

  • 感谢@Typed注解的解释。我是新的,还有更多内容!
【解决方案2】:

您的意思是:“取决于配置...”您何时决定使用什么?在编译时还是运行时?

有几种方法可以做到这一点:

1. @Alternative 和 beans.xml

使用 @Alternative 注释 SomeService 和 OtherService 并在 beans.xml 中使用

再次激活其中一个
<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>SomeService</class>
    </alternatives>

</beans>

2。有资格赛和制片人:

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

并用以下方式注释两个 Bean:

@First
@Stateless
public SomeService{ ...  }

现在您可以拥有如下所示的 Producer-Class:

@Dependent
public class ServiceProducer {

  @Inject
  @First
  private Service someService;

  @Inject
  @Second
  private Service otherService;

  @Produces
  @Default
  public Service getService() {
    switch (someCondition) {
        case SOME:
            return someService;
        case OTHER:
            return otherService;
        default:
            return null;
    }
  }   
}

最后将 Service 注入到你想使用的地方:

@Inject
Service service;

3.没有 Producer 但有 Qualifier-Annotations

您需要注释 SomeService 和 OtherService 才能使其工作。

@Inject
Instance<Service> services;

public void someBussinessMethod(){
    Annotation qualifier = someCondition ? new AnnotationLiteral<First>() {} : new AnnotationLiteral<Second>() {};
    Service s = services.select(qualifier).get();
}

4.没有限定符

在我看来,这是最丑陋和最慢的解决方案,但您可以迭代 Injectend 服务并由 Class 决定是否要使用它。

@Inject
Instance<Service> services;


public void doSomething() {

    Class clazz = someCondition ? SomeService.class : OtherService.class;

    Service first = services.stream().filter(s -> s.getClass() == clazz).findFirst().get();


}

可以在这里找到详细的解释: https://docs.jboss.org/cdi/learn/userguide/CDI-user-guide.html#injection

【讨论】:

    【解决方案3】:

    关于@kukeltje 的评论,我使用了@Produces 注释,如下所示:

    @ApplicationScoped
    public class MyProducer {
    
      @Inject
      private SomeService someService;
    
      @Inject
      private OtherService otherService;
    
      @Produces
      @ApplicationScoped
      public Service getService() {
        switch (someCondition) {
            case SOME:
                return someService;
            case OTHER:
                return otherService;
            default:
                return null;
        }
      }   
    }
    

    用法:

    @Stateless
    public class MyClass {
    
      @Inject
      private Service service;
    }
    

    【讨论】:

    • 能否请您添加注入@Produces Service 的代码段?我希望注入服务然后生成其中一个服务会产生关于歧义的错误,因为SomeServiceOtherService 都实现了Service,而@Produces Service 是第三个。所以我预计会发生冲突(但我不是专家)
    • 我添加了如何使用此注入的代码。到目前为止,我没有遇到任何问题。
    • 好的,您正在注入 EJB,也许这可以解释为什么没有“歧义”错误