一种可能的解决方案是在请求处理中提取重要的租户值,例如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 - 无论线程池如何都有效,但显然需要事务存在。
更新 14.12.2015
使用请求范围 bean 作为数据持有者的替代方法
RequestScoped 持有者
@RequestScoped
public class RequestDataHolder {
private String tenantId;
public String getTenantId() {
return this.tenantId;
}
public void setTenantId(String tenantId) {
this.tenantId = tenantId;
}
}
网络过滤器
从请求中提取值并将它们存储在我们的持有者中。
@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 容器正确集成。