1

我使用 HK2 作为 Jersey RESTful API 的一部分。我在一个多租户系统中工作,在我的大多数 API 调用中,租户是一个路径参数。我还有几个 DAO,它们目前在其构造函数中接受tenantId,例如:

public final class WidgetMapper {
    public WidgetMapper(final int tenantId) { .. }
    ..
}

我想使用 HK2 将我的 DAO 提供给我的应用程序的其他层。这样做的正确方法是什么?

  1. 将 DAO 更改为使用 setter 而不是构造函数参数。只有.. ick。这tenantId是 DAO 所需状态的一部分。

  2. 添加一个抽象层。创建<interface>MapperFactoryand MapperFactoryImpl,它有一个无参数构造函数和一堆 getter,例如getWidgetMapperand getGizmoMapper。只有..这似乎很麻烦。我宁愿不必维护这些额外的类。

  3. HK2 是否有一些神奇的方法可以在运行时将该 int 值注入 WidgetMapper 构造函数?然后我可以将tenantId 注入映射器,并将映射器注入我的其他类。

  4. ?? 其他HK2魔术?

4

2 回答 2

4

您需要从请求中的路径参数中提取租户 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不知道它在 Web 服务中使用的任何知识。

于 2014-09-23T22:36:09.313 回答
0

将 DAO 更改为使用 setter 而不是构造函数参数。只有.. ick。tenantId 是 DAO 所需状态的一部分。

如果您的 DAO 是单例,我看不出这将如何工作(或至少如何干净地完成)。

这样做的正确方法是什么?

IMO,我认为最好的方法是拥有 1) 单例 DAO 2) 某种类型的代理,当它们由 HK2 实例化时注入到 DAO 中,然后为当前线程提供正确的租户 ID。

我可以想到两种方法来做到这一点:

选项1:

我还没有尝试过,但我认为您可能可以通过构造函数、私有字段或设置器将 UriInfo 注入到您的 DAO 中。您可以从 UriInfo 实例中提取当前请求的租户 ID。

如果我是你,我会为我的 DAO 创建一个抽象类,将 UriInfo 注入私有字段。然后我会提供一个受保护的方法来从uriInfo.getPathParameters返回当前租户 ID

public 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魔术?

您可以编写自定义注入解析器

还有一个想法...

选项 3:

这个没有直接回答你的问题,因为它不使用 HK2 将租户 ID 注入 DAO,但我认为值得一提。

您可以实现自己的ContainerRequestFilter来获取租户 id 并将其提供给应用程序中的其他组件。

默认情况下,Jersey 将在解析资源方法之后但在实际调用方法之前调用过滤器。您可以从ContainerRequestContext获取 UriInfo ,获取租户 ID 路径参数,然后将该参数填充到您自己的线程局部变量中。然后,您可以在 DAO 中引用本地线程。同样,我建议在基础 DAO 类中添加一个受保护的方法来封装这个逻辑。

在我的大多数 API 调用中,租户是一个路径参数

或者,您可以使用NameBinding来控制上述行为。

如果您愿意,您可以使用常规的 ServletFilter 来实现选项 3。

笔记:

在我写完这个答案之后,我意识到我假设您对扩展ResourceConfig很满意,您知道如何获取ServiceLocator的实例,并且您对添加自己的 bindings 很满意。如果不是,请告诉我,我将编辑我的答案以提供更多详细信息。

于 2014-01-21T13:43:50.083 回答