Внедрение другого компонента в локальное развитие с помощью Quarkus - PullRequest
1 голос
/ 15 февраля 2020

В Spring и Micronaut есть очень лаконичные способы внедрения различных bean-компонентов в зависимости от среды / профиля, в котором выполняется приложение. Я пытаюсь сделать то же самое с Quarkus.

Я читал этот пост: https://quarkus.io/blog/quarkus-dependency-injection/. И этот процесс упоминается в этой статье StackOverflow: Как я могу переопределить компонент CDI в Quarkus для тестирования? . В этом последнем посте говорится: «создайте бин в тестовом каталоге».

Моя проблема немного другая. Я хотел бы ввести боб, когда в «разработке». В производстве я бы хотел, чтобы вводился компонент по умолчанию. Из документов я не вижу способа, чтобы приложение делало это различие.

Если у меня есть класс по умолчанию, подобный этому:

@DefaultBean
@ApplicationScoped
class ProdProvider : SomeProvider {}

И я хочу переопределить его следующим образом this:

@Alternative
@Priority(1)
class DevProvider : SomeProvider {}

Как сделать так, чтобы это происходило только в режиме разработки?

В одном случае у меня есть класс провайдера учетных данных, который настраивает эмулятор Google PubSub во время локальной разработки. В производстве я использую класс, который реализует тот же интерфейс, но настоящий поставщик учетных данных. Частный случай, который побудил меня задать этот вопрос, - это класс, который реализует один метод:

@ApplicationScoped
class VaultLoginJwtProvider : LoginJwtProvider {
  @ConfigProperty(name = "vault.tokenPath")
  private val jwtPath: String? = null

  companion object {
    val logger: Logger = LoggerFactory.getLogger("VaultTokenProvider")
  }

  override fun getLoginJwt(): Optional<String> {
    logger.info("Using Vault Login JWT")

    return try {
      Optional.of(String(Files.readAllBytes(Paths.get(jwtPath))).trim { it <= ' ' })
    } catch (e: Exception) {
      logger.error("Could not read vault token at $jwtPath")
      logger.error(e.printStackTrace().toString())
      Optional.empty()
    }
  }
}

Этот класс внедряется в другой класс с помощью инжектора конструктора:

@Singleton
class JwtServiceImpl(
  @RestClient val vaultClient: VaultClient,
  @Inject val loginJwtProvider: LoginJwtProvider
) {
  private var serviceJwt: String? = null

  companion object {
    val logger: Logger = LoggerFactory.getLogger("JwtServiceImpl")
  }

  private fun getLoginToken(): String? {
    val vaultLogin = VaultLogin(
      role = "user-service",
      jwt = loginJwtProvider.getLoginJwt().get()
    )

    val loginResponse = vaultClient.login(vaultLogin)

    return loginResponse.auth.clientToken
  }
}

I Вы хотели бы добавить больше «ложного» класса в процессе разработки, который просто возвращает строку stati c. Я мог бы использовать ProfileManager.getActiveProfile(), но это смешало меня с проблемами разработки в моей логике c. И я не чувствую, что это имеет место в моем скомпилированном рабочем коде.

Это возможно в Micronaut с использованием аннотации @Requires(env = ["dev", "test"]). Я кратко рассмотрел использование @Produces, но документы Oracle EE показались мне немного трудными для получения asp. Если это решение, я покопаюсь.

1 Ответ

1 голос
/ 12 марта 2020

Мое решение состоит в том, чтобы создать окончательный компонент самостоятельно внутри @javax.ws.rs.ext.Provider. Не так элегантно, как Micronaut @ Требуется , но все работает.

Обратите внимание, что экземпляр SomeProvider не является "компонентом", вы должны заботиться о жизненном цикле самостоятельно (внедрение зависимостей, PostConstruct, no PreDestroy, ...).

org.acme.SomeProvider. java

package org.acme;

import javax.enterprise.context.ApplicationScoped;

public interface SomeProvider {

  void providerMethod();

  @ApplicationScoped
  class ProdProviderRequirement {
    void foo() {}
  }

  class ProdProvider implements SomeProvider {

    private final ProdProviderRequirement prodProviderRequirement;

    ProdProvider(final ProdProviderRequirement prodProviderRequirement) {
      this.prodProviderRequirement = prodProviderRequirement;
    }

    @Override
    public void providerMethod() {
      prodProviderRequirement.foo();
    }
  }

  class DevProvider implements SomeProvider {
    @Override
    public void providerMethod() {}
  }
}

org.acme.SomeProviderFactory. java

package org.acme;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.ws.rs.ext.Provider;
import org.acme.SomeProvider.DevProvider;
import org.acme.SomeProvider.ProdProvider;
import org.acme.SomeProvider.ProdProviderRequirement;

@Provider
class SomeProviderFactory {

  SomeProvider someProvider;

  @Inject
  SomeProviderFactory(final ProdProviderRequirement prodProviderRequirement) {
    final var someCondition = true;
    someProvider = someCondition ? new DevProvider() : new ProdProvider(prodProviderRequirement);
  }

  @Produces
  @ApplicationScoped
  SomeProvider someProvider() {
    return someProvider;
  }
}
...