Внедрить услугу в зависимости от конфигурации в Java EE - PullRequest
2 голосов
/ 26 января 2020

Мое приложение в настоящее время использует класс, в который две службы внедряются с аннотацией @Inject.

@Stateless
public class MyClass {

  @Inject
  private SomeService someService;

  @Inject
  private OtherService otherService;
}

Обе службы очень похожи и обе расширяют абстрактный класс Service.

Вот что я пытаюсь сделать ...

Моя основная идея c заключается в том, что класс MyClass будет выглядеть примерно так:

@Stateless
public class MyClass {

  @Inject
  private Service service;
}

В зависимости от конфигурацию, которую приложение решает внедрить SomeService или OtherService

Пример:

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

Предоставляет ли Jave EE решение для этого?

Ответы [ 3 ]

2 голосов
/ 27 января 2020

Чтобы это работало, вам нужно убедиться, что все, что "делает" SomeService исключает Service из списка типов, которые он может сделать, и все, что "делает" OtherService, исключает Service из списка

Например, если SomeService - это простой управляемый компонент, вам нужно добавить к нему аннотацию @Typed(SomeService.class):

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

}

Если, с другой стороны, SomeService создается методом продюсера, вам придется сделать то же самое аналогично:

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

Аннотация @Typed ограничивает набор типов к тому, что дано, а не к тому, что выведено.

Если вы делаете это на обеих "конкретных" службах, тогда ваш getService() метод продюсера, как написано в вашем ответе выше, должен работать.

1 голос
/ 20 февраля 2020

Что вы подразумеваете под: «В зависимости от конфигурации ...» Когда вы решаете, что использовать? Во время компиляции или во время выполнения?

Есть несколько способов сделать это:

1. @Alternative и bean. xml

Аннотируйте SomeService и OtherService с помощью @Alternative и снова активируйте один из них в bean-компонентах. 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 {}

И аннотируйте оба компонента с помощью:

@First
@Stateless
public SomeService{ ...  }

Теперь вы можете иметь класс производителя, который выглядит следующим образом:

@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;
    }
  }   
}

И, наконец, введите Сервис, где вы хотите его использовать:

@Inject
Service service;

3. Без Producer, но с аннотациями-квалификаторами

Вам нужно Annotate 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 и решить класс, хотите ли вы его использовать.

@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

0 голосов
/ 27 января 2020

Что касается комментария @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;
}
...