Как я могу полностью переопределить ServiceLocator по умолчанию, используемый Джерси?

Я разрабатываю приложение, которое использует Джерси (2.5) в качестве внешнего интерфейса REST и Jetty в качестве встроенного сервера HTTP (S), оба так называемым «встроенным» способом, например. не прибегая к созданию .war и его развертыванию, а через программную настройку обработчиков, ресурсов, инъекций...

Я хотел бы каким-то образом переопределить HK2 ServiceLocator, который используется на стороне сервера Джерси, или, возможно, предоставить этому локатору службы родителя для разрешения зависимостей, которые определены вне части REST приложения. Из того, что я вижу в коде, это кажется невозможным: экземпляр ServiceLocator создается внутри ApplicationHandler посредством вызова Injections:

if (customBinder == null) {
        this.locator = Injections.createLocator(new ServerBinder(application.getProperties()), new ApplicationBinder());
    } else {
        this.locator = Injections.createLocator(new ServerBinder(application.getProperties()), new ApplicationBinder(),
                                                customBinder);
    }

И код в Injections говорит мне следующее:

 public static ServiceLocator createLocator(Binder... binders) {
    return _createLocator(null, null, binders);
 }

что означает, что вновь созданный локатор службы имеет произвольно сгенерированное имя и не имеет родителя.

Есть ли (чистый) способ изменить это поведение, чтобы я ввел свой собственный ServiceLocator в качестве родителя приложения?


person insitu    schedule 22.04.2014    source источник
comment
Почему вы не можете просто добавить свои услуги в Джерси ServiceLocator?   -  person jwells131313    schedule 23.04.2014
comment
Потому что мои службы обнаруживаются/подключаются до того, как я запускаю контейнер. Существует загрузчик служб, который загружает модули с элементами DI, и я хотел бы, чтобы эти модули могли вносить ресурсы, не завися от HK2, хотя в настоящее время он реализован в HK2 за кулисами. Я действительно не понимаю, почему в Джерси так тяжело.   -  person insitu    schedule 23.04.2014
comment
Сначала я думал, что вы можете использовать AliasDescriptors, но я думаю, что здесь нужна небольшая функция, чтобы сделать эту работу лучше. Я пытаюсь сделать это проще.   -  person jwells131313    schedule 24.04.2014
comment
То, что я делаю сейчас, чем я действительно не горжусь, - это перегрузка класса Injections на уровне исходного кода для внедрения родительского ServiceLocator.   -  person insitu    schedule 24.04.2014
comment
Является ли ваш базовый локатор службы чем-то, что я также должен отслеживать динамически? Я спрашиваю, потому что я могу легко создать утилиту, которая копирует текущий набор сервисов из одного локатора в другой (через псевдоним, а не копировать на самом деле), или я могу создать преобразователь JIT, который будет работать лучше, если ваш базовый локатор может менять сервисы. со временем, но который тогда будет работать только для инъекций, а не для сырых поисков.   -  person jwells131313    schedule 24.04.2014
comment
Не уверен, что понимаю вопрос. Разве нельзя предоставить ServiceLocator при построении ResourceConfig, который будет использоваться в качестве базы для разрешения зависимостей?   -  person insitu    schedule 24.04.2014
comment
@ jwells131313 jwells131313 вы случайно не реализовали эту функцию? Я попытался реализовать его извне, но не смог ни получить список дескрипторов из ServiceLocator, ни понять, как создать AliasDescriptor из существующего дескриптора.   -  person Natan    schedule 29.01.2015
comment
Мы решили проблему по-другому. Не должно быть сложно получить все дескрипторы из одного ServiceLocator и отразить их в другом. В частности, есть метод BuilderHelper.allFilter, который можно передать в getAllServices(Filter f), который предоставит вам все службы в ServiceLocator. Оттуда не должно быть слишком сложно добавить все службы из одной службы в другую ServiceLocator. Функция, которая могла бы сделать это возможным с помощью преобразователя JIT, — это java.net/jira/browse/ НК2-170. Мы отложили реализацию этого из-за некоторых проблем с безопасностью.   -  person jwells131313    schedule 29.01.2015
comment
Я также добавил это: java.net/jira/browse/HK2-242 в отслеживать общий запрос, который должен отражать службы одного ServiceLocator в другом ServiceLocator   -  person jwells131313    schedule 29.01.2015


Ответы (3)


Я знаю, что этот ответ немного запоздал. Я боролся с той же проблемой, но в рамках Dropwizard. После некоторой отладки я увидел несколько строк кода, которые меня очень порадовали!

final ServiceLocator locator = (ServiceLocator) webConfig.getServletContext()
            .getAttribute(ServletProperties.SERVICE_LOCATOR);

Этот фрагмент кода находится внутри конструктора jerseyes WebComponent. Таким образом, решение состоит в том, чтобы предоставить ServletProperties.SERVICE_LOCATOR вашему ServletContext. В среде Dropwizard я достиг этого, выполнив

environment.getApplicationContext().getAttributes().setAttribute(ServletProperties.SERVICE_LOCATOR, locator);
person Woodbird    schedule 02.04.2015
comment
Интересно, не прибегает ли DropWizard к тому же самому хаку, который я должен был сделать внутри, чтобы он открывал вам локатор. - person insitu; 03.04.2015

У нас аналогичная настройка, и мне удалось заставить нашу архитектуру работать с помощью нового bridgeServiceLocator.

EDIT: обратите внимание, что текущая реализация моста HK2 означает, что экземпляры Singleton являются локальными только для ServiceLocator, в котором они созданы, а это означает, что мостовые архитектуры могут содержать более одного экземпляра службы Singleton. См. этот вопрос для получения дополнительной информации и возможного обходного/альтернативного подхода.

РЕДАКТИРОВАНИЕ № 2: исправлена ​​вышеупомянутая ошибка в мосте ServiceLocator. Исправление будет в hk2 2.5.0-b07 или более поздней версии

По сути, я создал реализацию Feature для настройки моста и зарегистрировал ее в Джерси (в нашем случае через ServletContainer).

public class InjectionBridge implements Feature
{
  private static ServiceLocator _applicationServiceLocator;

  private final ServiceLocator _serviceLocator;

  @Inject
  private InjectionBridge(ServiceLocator serviceLocator)
  {
    _serviceLocator = serviceLocator;
  }

  @Override
  public boolean configure(FeatureContext context)
  {
    if (_applicationServiceLocator != null)
      ExtrasUtilities.bridgeServiceLocator(_serviceLocator, _applicationServiceLocator);
    return true;
  }

  public static void setApplicationServiceLocator(ServiceLocator applicationServiceLocator)
  {
    _applicationServiceLocator = applicationServiceLocator;
  }
}

Затем из кода приложения вызывается setApplicationServiceLocator с ServiceLocator, созданным приложением для управления ресурсами приложения.

Это означает, что реализации службы RESTful на Джерси теперь могут просто объявлять поля с аннотацией @Inject и получать доступ к этим внедренным ресурсам для запросов на обслуживание.

person Nathan Brown    schedule 22.04.2015

Вы не можете сделать один ServiceLocator родительским для другого после создания ServiceLocator.

Однако, начиная с hk2 2.4.0-b11, будет возможность иметь мост ServiceLocator -> ServiceLocator. Таким образом, все услуги из вашего ServiceLocator могут быть помещены в ServiceLocator Джерси (или любой другой ServiceLocator). Это API: bridgeServiceLocator. Он находится в модуле hk2-extras hk2.

В hk2 2.4.0-b10 есть версия, которая должна работать, полностью протестированная и задокументированная функция будет в hk2 2.4.0-b11.

person jwells131313    schedule 09.02.2015
comment
jwells131313 У ​​меня возникла проблема с использованием этого моста, изложенные в этом вопросе здесь. Я был бы признателен за любой отзыв, который вы можете дать. - person Nathan Brown; 08.07.2015