我使用HK2作为Jersey RESTful API的一部分。我在多租户系统中工作,在我的大多数API调用中,租户都是路径参数。我还有几个DAO,它们在构造函数中接受tenantId,例如:
public final class WidgetMapper {
public WidgetMapper(final int tenantId) { .. }
..
}
我想使用HK2将我的DAO提供给我的应用程序的其他层。什么是正确的方法?
更改DAO以使用setter而不是构造函数参数。只有.. ick。 tenantId
是DAO所需状态的一部分。
添加一个抽象层。创建<interface>MapperFactory
和MapperFactoryImpl
,其中包含无参数构造函数和一堆getter,例如getWidgetMapper
和getGizmoMapper
。只有......这看起来很麻烦。我宁愿不必维持这些额外的课程。
HK2在运行时将这个int值注入WidgetMapper构造函数是否有一些神奇的方法?然后我可以将tenantId注入mapper,将mapper注入我的其他类。
??其他HK2魔术?
答案 0 :(得分:3)
您需要从请求中的path参数中提取租户ID,因此只要可以为每个请求实例化DAO,就可以实现Factory
。
public WidgetMapperFactory implements Factory<WidgetMapper> {
private final ContainerRequestContext containerRequestContext;
@Inject
public WidgetMapperFactory(ContainerRequestContext containerRequestContext) {
this.containerRequestContext = containerRequestContext;
}
public WidgetMapper provide() {
UriInfo uriInfo = containerRequestContext.getUriInfo();
List<String> matchedUris = uriInfo.getMatchedURIs();
int tenantId = 1; // Actually work it out from the matched URIs
return new WidgetMapper(tenantId);
}
public void dispose() {
// Do any clean up you need
}
}
然后绑定工厂:
public MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(new AbstractBinder() {
@Override
protected void configure() {
bindFactory(WidgetMapperFactory.class).to(WidgetMapper.class).in(RequestScoped.class);
}
});
}
}
然后,您可以在WidgetMapper
课程中注入Resource
,WidgetMapper
不知道它在网络服务中的使用情况。
答案 1 :(得分:0)
更改DAO以使用setter而不是构造函数参数。 只有.. ick。 tenantId是DAO所需状态的一部分。
如果您的DAO是单身人士,我不知道这是如何工作的(或至少如何干净利落地完成)。
这样做的正确方法是什么?
IMO,我认为最好的方法是拥有1)单例DAO 2)某些类型的代理在HK2实例化时注入DAO,然后为当前线程提供正确的租户ID。
我可以想到两种方法:
选项1:
我还没有尝试过,但我认为你可以可能通过构造函数,私有字段或setter将UriInfo注入你的DAO。您可以从UriInfo实例中提取当前请求的租户ID。
如果我是你,我会为我的DAO创建一个抽象类,将UriInfo注入私有字段。然后我会提供一个受保护的方法来从uriInfo.getPathParameters
返回当前的租户IDpublic abstract class AbstractDao {
// jersey/hk2 provides a proxy that references the current thread-bound request
@Context
private UriInfo info;
protected int getTenantId()
{
// always returns the tenant id for the current request. TODO: add
// logic to handle calls that don't have a tenant id.
return Integer.valueOf(info.getPathParameters.getFirst("tenantId");
}
}
选项2:
??其他HK2魔术?
你可以写一个custom injection resolver。
还有一个想法......
选项3:
这个问题没有直接回答你的问题,因为它没有使用HK2将租户ID注入DAO,但我认为值得一提。
您可以实现自己获得租户ID的ContainerRequestFilter,并将其提供给您应用中的其他组件。
默认情况下,Jersey会在解析资源方法之后但在实际调用该方法之前调用过滤器。您可以从ContainerRequestContext获取UriInfo,获取租户ID路径参数,然后将该参数填充到您自己的线程局部变量中。然后,您可以在DAO中引用本地线程。同样,我建议在基础DAO类中添加一个受保护的方法来封装这个逻辑。
在我的大多数API调用中,租户都是路径参数
或者,您可以使用NameBinding来控制上述行为。
如果您愿意,可以使用常规的ServletFilter来实现选项3。
注意:强>
在我写完这个答案之后,我意识到我认为你很乐意延长ResourceConfig,你知道如何获得ServiceLocator的实例,并且你是comfortable with adding your own bindings。如果您不是,请告诉我,我将编辑我的答案以提供更多详细信息。