将运行时(元)数据传递给 CDI 中的生产者方法
Passing Runtime (Meta)Data To Producer Method in CDI
我正在维护一个 multi-tenant 应用程序,其中请求的特殊元数据(headers、参数)标识特定租户。每个租户在系统中都有覆盖某些默认值的自定义配置。配置来自以 EJB 为前端的 cache-augmented 数据库。要成功查找此类自定义配置,需要密钥和租户标识符。如果租户标识符不存在,则单独使用密钥来检索密钥条目的默认值。
我想从接收这些请求的远程接口(servlet、web 服务等)中检索此类标识符和设置上下文(例如,将属性放入 EJBContext
),以便生产者方法可以利用设置适当的 bean 来为每个租户的客户提供服务。理想情况下,在这种情况下,我也希望尽可能合理地支持 CDI 而不是 EJB。
我正在按照以下策略思考,但我卡住了。
- 创建一个
@Config
限定符,以便 CDI 容器解析为配置生产者。
- 创建一个
@Key(String)
配置注解,通过该注解可以获得所需配置条目的查找键。
- 创建一个将
InjectionPoint
作为参数的 Producer 方法。 InjectionPoint
允许获取 @Key
注释、目标字段的声明类型以及声明此注入字段的 class(包含 class)。如果 InjectionPoint
允许我获得封闭 class 的一个实例,那将是一个不错的场景。但是考虑一下,这没有意义,因为在所有依赖项都被 created/located 并注入之前,实例还没有准备好。
CDI 不适合这种情况吗?如何最好地实施?
一种可能的解决方案是在请求处理中提取重要的租户值,例如ServletFilter
或一些拦截器并将其存储在 ThreadLocal
容器中。这仅在两个组件(e.q。过滤器和 CDI 生产者)在同一线程中执行时才有效 - 因此您可能 运行 遇到 EJB 问题。
您可以在 @Produces
方法中检索租户标识符,并根据 @Key
注释值和租户 ID return 配置条目。
一些伪解:
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;
}
}
WebFilter
从请求中提取值并将它们存储在我们的 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 生产商
使用数据容器并生成注入点的预期值。
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 可以注入任何 CDI、EJB、JAXRS 或 Servlet 组件,从而允许将变量从 WEB 上下文传递到其他上下文。
注意:此解决方案需要根据 CDI 规范正确集成 CDI 容器与 EJB 和 WEB 容器。
我正在维护一个 multi-tenant 应用程序,其中请求的特殊元数据(headers、参数)标识特定租户。每个租户在系统中都有覆盖某些默认值的自定义配置。配置来自以 EJB 为前端的 cache-augmented 数据库。要成功查找此类自定义配置,需要密钥和租户标识符。如果租户标识符不存在,则单独使用密钥来检索密钥条目的默认值。
我想从接收这些请求的远程接口(servlet、web 服务等)中检索此类标识符和设置上下文(例如,将属性放入 EJBContext
),以便生产者方法可以利用设置适当的 bean 来为每个租户的客户提供服务。理想情况下,在这种情况下,我也希望尽可能合理地支持 CDI 而不是 EJB。
我正在按照以下策略思考,但我卡住了。
- 创建一个
@Config
限定符,以便 CDI 容器解析为配置生产者。 - 创建一个
@Key(String)
配置注解,通过该注解可以获得所需配置条目的查找键。 - 创建一个将
InjectionPoint
作为参数的 Producer 方法。InjectionPoint
允许获取@Key
注释、目标字段的声明类型以及声明此注入字段的 class(包含 class)。如果InjectionPoint
允许我获得封闭 class 的一个实例,那将是一个不错的场景。但是考虑一下,这没有意义,因为在所有依赖项都被 created/located 并注入之前,实例还没有准备好。
CDI 不适合这种情况吗?如何最好地实施?
一种可能的解决方案是在请求处理中提取重要的租户值,例如ServletFilter
或一些拦截器并将其存储在 ThreadLocal
容器中。这仅在两个组件(e.q。过滤器和 CDI 生产者)在同一线程中执行时才有效 - 因此您可能 运行 遇到 EJB 问题。
您可以在 @Produces
方法中检索租户标识符,并根据 @Key
注释值和租户 ID return 配置条目。
一些伪解:
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;
}
}
WebFilter
从请求中提取值并将它们存储在我们的 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 生产商 使用数据容器并生成注入点的预期值。
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 可以注入任何 CDI、EJB、JAXRS 或 Servlet 组件,从而允许将变量从 WEB 上下文传递到其他上下文。
注意:此解决方案需要根据 CDI 规范正确集成 CDI 容器与 EJB 和 WEB 容器。