在 Jersey (HK2) 中按名称动态查找服务

Dynamically find service by name in Jersey (HK2)

在我的应用程序中,我需要根据某些用户输入获取不同的实现。

因为我想充分利用 HK2 我想用 Jersey/HK2 提供的方法解决这个问题。

到目前为止,我所做的只是通过接口注入服务,这些接口在启动时使用 ApplicationConfigApplicationBinder:

绑定到实现
@javax.ws.rs.ApplicationPath("api")
public class ApplicationConfig extends ResourceConfig
{
    public ApplicationConfig()
    {
        super();
        packages(true, "my.package");
        register(new ApplicationBinder());
        register(....);
        ....
    }
}

public class ApplicationBinder extends AbstractBinder
{
    @Override
    protected void configure()
    {
        bind(ServletTemplateLoader.class).to(TemplateLoader.class);
        bindAsContract(JobsImpl.class);
        bindAsContract(JobInputAppender.class);
        bindAsContract(ParamNameMapper.class);
        bind(RedisJobRepository.class).to(JobRepositoryInterface.class);
        ....
    }

但是现在,我需要根据用户输入动态获取实现。有 25 种不同的实现都使用相同的接口。

这意味着,我不能再简单地使用 bind.to 方法。相反,我认为我需要使用 bindAsContract.

单独注册它们

但是,我该如何编写一个 method/class 来为任何给定的输入(来自用户)提供正确的实现?

本质上,我需要一个如下所示的方法:

public interface MyInterface {}
public class Type1Impl implements MyInterface {} // registered with `bindAsContract`

public MyInterface getImplementation(final String type_)
{
    switch (type_) {
        case "type1":
            return // what to do here to get "my.package.Type1Impl" instance?
        case "type":
            ....
    }
}

我需要来自 HK2 的实例,因为 Impl 也使用注入服务,所以我不能简单地即时创建一个新实例。

所以在搜索了几个小时没有得到答案后,我感到很沮丧并转回原处,想 "ok just try to do the most obvious thing you can think of"。

在 DI 的情况下,就是告诉容器给我我想要的东西。

事实证明,这是可行的,而且几乎微不足道...

public interface MyInterface {}
public class Type1Impl implements MyInterface {}
public class Type2Impl implements MyInterface {}

@javax.ws.rs.ApplicationPath("api")
public class ApplicationConfig extends ResourceConfig
{
    public ApplicationConfig()
    {
        super();
        packages(true, "my.package");
        register(new ApplicationBinder());
    }
}

public class ApplicationBinder extends AbstractBinder
{
    @Override
    protected void configure()
    {
        bindAsContract(ImplementationGetter.class);
        bind(Type1Impl.class).to(MyInterface.class).named("type1");
        bind(Type2Impl.class).to(MyInterface.class).named("type2");
    }
}

public class ImplementationGetter {
    @Inject
    private ServiceLocator _locator;

    public MyInterface getImplementation(final String type_)
    {
        switch (type_) {
            case "type1":
                return _locator.getService(MyInterface.class, "type1");
            case "type2":
                return _locator.getService(MyInterface.class, "type2");
        }
    }
}

我认为使用 IterableProvider 会有更好的答案。基本上你可以在你的一项服务中做到这一点:

public class ImplementationGetter {
  @Inject
  private IterableProvider<MyInterface> interfaceProvider;

  public MyInterface getImplementation(final String type_) {
    return interfaceProvider.named(type_).get();
  }
}

希望对您有所帮助!