java - 将运行时(元)数据传递给 CDI 中的生产者方法

标签 java jakarta-ee ejb cdi

我正在维护一个 Multi-Tenancy 应用程序,其中关于请求的特殊元数据( header 、参数)标识特定租户。每个租户在系统中都有覆盖某些默认值的自定义配置。配置来自以 EJB 为前端的缓存增强数据库。要成功查找此类自定义配置,需要 key 和租户标识符。如果租户标识符不存在,则单独使用 key 来检索 key 条目的默认值。

我想从接收这些请求的远程接口(interface)(servlet、web 服务等)中检索此类标识符和设置上下文(例如,将属性放入 EJBContext 中),以便生产者方法可以利用设置适当的 beans 来为每个租户的客户提供服务。理想情况下,在这种情况下,我也希望尽可能合理地支持 CDI 而不是 EJB。

我在考虑以下策略,但我卡住了。

  1. 创建一个 @Config 限定符,以便 CDI 容器解析为配置生产者。
  2. 创建一个@Key(String)配置注解,通过该注解可以获得所需配置条目的查找键。
  3. 创建一个将 InjectionPoint 作为参数的 Producer 方法。 InjectionPoint 允许获取 @Key 注释、目标字段的声明类型以及声明此注入(inject)字段的类(封闭类)。如果 InjectionPoint 允许我获得封闭类的一个实例,那将是一个不错的场景。但是考虑一下,这没有意义,因为在创建/定位并注入(inject)所有依赖项之前,实例还没有准备好。

CDI 不适合这种情况吗?如何最好地实现?

最佳答案

一种可能的解决方案是在请求处理中提取重要的租户值,例如ServletFilter 或一些拦截器并将其存储在 ThreadLocal 持有者中。这仅在两个组件(例如过滤器和 CDI 生产者)在同一线程中执行时才有效 - 因此您可能会遇到 EJB 问题。
您可以在 @Produces 方法中检索租户标识符,并根据 @Key 注释值和租户 ID 返回配置条目。

一些伪解决方案:

ThreadLocal 持有者

public class ThreadLocalHolder {

  private static ThreadLocal<String> tenantIdThreadLocal = new ThreadLocal<>();

  public static String getTenantId(){
    return tenantIdThreadLocal.get();
  }
  public static void setTenantId(String tenantid){
    return tenantIdThreadLocal.set(tenantid);
  }
}

租户提取的请求过滤器

@WebFilter(value = "/*")
public class TenantExtractorFilter implements Filter {
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    //obtain tenant id, and store in threadlocal
    ThreadLocalHolder.setTenantId(req.getHeader("X-TENANT"));
    chain.doFilter(request, response);
  }
}

配置条目生产者

public class Producer {

  //get a hold of some DAO or other repository of you config
  private ConfigRepository configRepo;

  @Produces
  @Config
  public String produceConfigEntry(InjectionPoint ctx) {
    Key anno = //get value of @Key annotation from the injection point, bean, property...
    String tenantId = ThreadLocalHolder.getTenantId();
    // adjust to your needs
    return configRepo.getConfigValueForTenant(anno.value(), tenantId);
  }
}

如果 ThreadLocal 不是一个选项,请查看 javax.transaction.TransactionSynchronizationRegistry - 它的工作与线程池无关,但显然需要存在事务。

2015 年 12 月 14 日更新
使用请求范围 bean 作为数据持有者的替代方法

RequestScoped 持有者

@RequestScoped
public class RequestDataHolder {
  private String tenantId;

  public String getTenantId() {
    return this.tenantId;
  }

  public void setTenantId(String tenantId) {
    this.tenantId = tenantId;
  }
}

网络过滤器

从请求中提取值并将它们存储在我们的 holder 中。

@WebFilter(value = "/*")
public class TenantExtractorFilter implements Filter {

  @Inject private RequestDataHolder holder;

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    //obtain tenant id, and store in threadlocal
    holder.setTenantId(req.getHeader("X-TENANT"));
    chain.doFilter(request, response);
  }
}

CDI 制作人 使用数据持有者并为注入(inject)点生成预期值。

public class Producer {

  //get a hold of some DAO or other repository of you config
  private ConfigRepository configRepo;
  @Inject
  private RequestDataHolder dataHolder; 

  @Produces
  @Config
  public String produceConfigEntry(InjectionPoint ctx) {
    Key anno = //get value of @Key annotation from the injection point, bean, property...
    String tenantId = holder.getTenantId();
    // adjust to your needs
    return configRepo.getConfigValueForTenant(anno.value(), tenantId);
  }
}

我们的 RequestDataHolder bean 可以注入(inject)到任何 CDI、EJB、JAXRS 或 Servlet 组件中,从而允许将变量从 WEB 上下文传递到其他上下文。

注意:此解决方案需要根据 CDI 规范将 CDI 容器与 EJB 和 WEB 容器正确集成。

关于java - 将运行时(元)数据传递给 CDI 中的生产者方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34159078/

相关文章:

java - 子类的构造函数

java - 图像不会显示在 JPanel 上

java - 为什么 javax.ejb.AsyncResult.isDone 总是抛出异常?

java - 异步处理 J2EE

jpa - JEE6 war 打包成几个模块

java - EJB + JSF (Primefaces) Maven -> 在 EJB 中使用 primefaces jar -> 在 Jboss、Eclipse 上部署期间出现异常

java - 使用 Spark 高效读取 PDF/文本/word 文件

java - 检查列表中是否包含整数值时如何处理 HQL 查询中的空值

java - 无法 Swagger 工作(wildfly 9)

Java EE 事务回滚