Динамически найти сервис по имени в Джерси (HK2) - PullRequest
0 голосов
/ 25 июня 2019

В моем приложении мне нужно приобрести другую реализацию, зависящую от вводимых пользователем данных.

Поскольку я хочу в полной мере использовать преимущества HK2, я хочу решить эту проблему с помощью методов, предоставляемых Jersey / HK2.

До сих пор все, что я когда-либо делал, это инжектировал сервисы через интерфейсы, которые были привязаны к реализациям при запуске, используя ApplicationConfig и ApplicationBinder:

@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.

Но тогда, как мне написать метод / класс, который для любого данного ввода (от пользователя) предоставит мне правильную реализацию?

По сути, мне нужен метод, который выглядит следующим образом:

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 также использует внедренные сервисы, поэтому я не могу просто создатьновый экземпляр на лету.

Ответы [ 2 ]

2 голосов
/ 26 июня 2019

Я думаю, что есть лучший ответ, используя IterableProvider . В основном вы можете сделать это в одном из ваших сервисов:

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

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

Надеюсь, это поможет!

0 голосов
/ 25 июня 2019

Так что после нескольких часов поиска без ответа я расстроился и обернулся, подумав: «Хорошо, просто постарайся сделать самое очевидное, о чем ты только можешь подумать».

Что в случае с 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");
        }
    }
}
...