Как заставить HK2 действовать как Guice для внедрения классов, которые не были настроены явно?

У меня есть проект, использующий Jersey 2.25HK2 2.5-b30). Первоначально я использовал HK2-Guice Bridge. Однако в некоторых случаях это неожиданно дает сбой (в частности, случаи, когда строки аннотируются пользовательской аннотацией, настроенной в Guice, будут работать нормально, когда Guice выполняет инъекцию, но молча терпят неудачу, когда это делает HK2). Поскольку один и тот же объект может действовать по-разному в зависимости от того, как он был введен, я боюсь использовать их вместе.

Сейчас я переключаю все на использование HK2, но, к сожалению, похоже, что HK2 терпит неудачу в некоторых случаях, когда Guice преуспеет. В частности, кажется, что HK2 не любит внедрение, когда тип не был настроен явно. Guice был счастлив просто создать новый экземпляр этих классов и внедрить рекурсивно, но HK2 не очень. Например,

1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=TimeRangeRequestValidator,parent=GetWatchlistEventsImpl,qualifiers={},position=-1,optional=false,self=false,unqualified=null,1218743359)

Как видите, сообщение об ошибке не очень полезно. Он должен быть в состоянии создать TimeRangeRequestValidator, который ссылается на некоторые другие объекты, все из которых Guice смог создать без проблем. Есть ли какой-то список различных поведений между HK2 и Guice, чтобы я мог отследить, почему это не работает?

Обратите внимание, что TimeRangeRequestValidator — это класс (не интерфейс), аннотированный @Singleton, который имеет общедоступный конструктор по умолчанию и поле, аннотированное Inject. У Гайса не было проблем с его созданием.


person Robert Fraser    schedule 25.09.2017    source источник


Ответы (2)


Вы также можете использовать жадный JustInTimeResolver. Я написал один ниже:

@Singleton
@Visibility(DescriptorVisibility.LOCAL)
public class GreedyResolver implements JustInTimeInjectionResolver {
    private final ServiceLocator locator;

    @Inject
    private GreedyResolver(ServiceLocator locator) {
        this.locator = locator;
    }

    @Override
    public boolean justInTimeResolution(Injectee failedInjectionPoint) {
        Type type = failedInjectionPoint.getRequiredType();
        if (type == null) return false;

        Class<?> clazzToAdd = null;
        if (type instanceof Class) {
            clazzToAdd = (Class<?>) type;
        }
        else if (type instanceof ParameterizedType) {
            Type rawType = ((ParameterizedType) type).getRawType();
            if (rawType instanceof Class) {
                clazzToAdd = (Class<?>) rawType;
            }
        }

        if (clazzToAdd == null) return false;
        if (clazzToAdd.isInterface()) return false;

        ServiceLocatorUtilities.addClasses(locator, clazzToAdd);
        return true;
    }

}

Вы должны быть осторожны при использовании вышеупомянутого распознавателя, так как он добавит в ваш ServiceLocator вещи, которых вы, возможно, не ожидали. Это также, вероятно, не будет хорошо работать с такими вещами, как строки или другие подобные типы. Тем не менее, может работать для вашего варианта использования.

Не будет работать, если ваша точка внедрения внедряет интерфейс!

person jwells131313    schedule 27.09.2017
comment
Ага; это похоже на то, что мне нужно. Я добавлю несколько проверок, чтобы убедиться, что я действительно ввожу то, что хочу вводить. Я думаю, что это именно то, что использует мост Guice, но, по крайней мере, это явно дает больше видимости и контроля. - person Robert Fraser; 27.09.2017

Есть несколько дополнительных шагов, чтобы настроить HK2 для автоматического заполнения Сервисы:

  1. Убедитесь, что вы аннотировали свои интерфейсы с помощью @Contract, а свои реализации с помощью @Service.
  2. Вам нужно запустить генератор метаданных HK2 во время сборки. Это создает служебные файлы, которые нужны HK2 во время выполнения, чтобы определить, какие классы реализуют интерфейсы контрактов.
  3. Используйте ServiceLocatorUtilities.createAndPopulateServiceLocator() для получения экземпляра ServiceLocator.

Обратите внимание, что особенности или то, как это будет работать для вас, зависят от того, какие рамки (например, трикотаж) вы используете. См. Использование HK2 с Джерси.

person John Farrelly    schedule 25.09.2017