HK2和Impls带有构造函数参数

时间:2014-01-21 12:35:25

标签: java jersey hk2

我使用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>MapperFactoryMapperFactoryImpl,其中包含无参数构造函数和一堆getter,例如getWidgetMappergetGizmoMapper。只有......这看起来很麻烦。我宁愿不必维持这些额外的课程。

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

  4. ??其他HK2魔术?

2 个答案:

答案 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课程中注入ResourceWidgetMapper不知道它在网络服务中的使用情况。

答案 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

返回当前的租户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魔术?

你可以写一个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。如果您不是,请告诉我,我将编辑我的答案以提供更多详细信息。