在使用 Quarkus 进行本地开发期间注入不同的 bean

Injecting a different bean during local development with Quarkus

使用 Spring 和 Micronaut,有非常简洁的方法来注入不同的 bean,具体取决于 environment/profile 应用程序 运行 所在的内容。我正在尝试做同样的事情与夸尔库斯。

我读过这个 post:https://quarkus.io/blog/quarkus-dependency-injection/. And the process is alluded to in this Whosebug post: 。最后一个 post 表示 "create bean in test directory"。

我的问题略有不同。我想在 "development" 中注入一个 bean。在生产中,我想要注入默认 bean。从文档中,我看不到让应用程序做出这种区分的方法。

如果我有这样的默认 class:

@DefaultBean
@ApplicationScoped
class ProdProvider : SomeProvider {}

我想像这样覆盖它:

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

我怎样才能让它只在开发模式下发生?

在一个案例中,我有一个凭证提供程序 class,它在本地开发时设置了 Google 的 PubSub 模拟器。在生产中,我使用 class 实现相同的接口,但它是一个真正的凭证提供者。导致我问这个问题的特殊情况是 class 实现了一种方法:

@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()
    }
  }
}

通过构造函数注入将 class 注入另一个 class:

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

我想在开发中注入更多 "mock" class 只是 returns 静态字符串。我可以使用 ProfileManager.getActiveProfile(),但这让我将开发问题混入了我的逻辑中。而且我觉得这在我编译的生产代码中没有任何位置。

这可以在 Micronaut 中使用注解 @Requires(env = ["dev", "test"]) 实现。我确实简要地查看了使用 @Produces,但 Oracle EE 文档对我来说似乎有点难以掌握。如果那是解决方案,我会深入研究。

我的解决方案是在 @javax.ws.rs.ext.Provider. Not as elegant as Micronaut @Requires 中自行创建最终的 bean,但它确实有效。

请注意 SomeProvider 的实例不是 "bean",您必须自己关心生命周期(依赖注入、PostConstruct、无 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;
  }
}

如果其他人遇到此问题,请按以下步骤操作:https://quarkus.io/guides/cdi-reference#enabling-beans-for-quarkus-build-profile

例如:

import javax.enterprise.inject.Produces;

import com.oi1p.common.EmailSender;
import com.oi1p.common.ErrorEmailSender;
import com.oi1p.common.LogOnlyEmailSender;

import io.quarkus.arc.DefaultBean;
import io.quarkus.arc.profile.IfBuildProfile;

@ApplicationScoped
public class Producers {

  @Produces
  @IfBuildProfile("dev")
  public EmailSender logOnlyEmailSender() {
    return new LogOnlyEmailSender();
  }

  @Produces
  @DefaultBean
  public EmailSender errorEmailSender() {
    // TODO: implement a real email sender.  This one explodes when poked.
    return new ErrorEmailSender();
  }

}