Подтвердить что ты не робот

Внедрить массив объектов в Guice

В Guice я хотел бы сделать что-то похожее на следующее:

public MyClass {

    private final InjectedObject[] injectedObjects;

    @Inject
    public MyClass(InjectedObject[] injectedObjects) {
        this.injectedObjects=injectedObjects;
    }
}

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

public MyClass {

    private final InjectedObject[] injectedObjects;

    @Inject
    public MyClass(InjectedObjectProvider injectedObjectProvider) {
        this.injectedObjects=injectedObjectProvider.getArrayOfInjectedObjects(5);
    }
}

... но мне было интересно, был ли другой маршрут более элегантным?

4b9b3361

Ответ 1

Мне любопытно, почему вы хотите, чтобы несколько объектов создавались с нетерпением. Возможно, вам удастся внедрить Provider<InjectedObject> и вызвать Provider.get() каждый раз, когда вам нужен экземпляр. Если вам действительно нужно 5, вы можете построить их в цикле:

public MyClass {
  private final List<InjectedObject> injectedObjects;

  @Inject
  public MyClass(Provider<InjectedObject> injectedObjectProvider) {
    injectedObjects = new ArrayList<InjectedObject>();
    for (int i = 0; i < 5; i++) {
      injectedObjects.add(injectedObjectProvider.get());
    }
  }
}

Ответ 2

Не уверен, что это соответствует вашим потребностям, но Multibindings работал у меня, когда мне нужно было вводить несколько элементов одного типа (это производит набор, хотя).

Ответ 3

Одним из вариантов было бы ввести Provider<InjectedObject> в ваш класс, как отметил Джесси:

public class MyClass {
  private final List<InjectedObject> injectedObjects;

  @Inject
  public MyClass(Provider<InjectedObject> injectedObjectProvider) {
    List<InjectedObject> objects = new ArrayList<InjectedObject>();
    for (int i = 0; i < 5; i++) {
      objects.add(injectedObjectProvider.get());
    }
    injectedObjects = Collections.unmodifiableList(objects);
  }
}

Выполнение этого может быть проблематичным. Если InjectedObject имеет область @Singleton или @RequestScoped, то каждый раз, когда вы вызываете injectedObjectProvider.get(), вы получите ту же ссылку. Другая проблема с введением Provider для этого - это не будет понятно из API, что MyClass зависит от нескольких экземпляров InjectedObject. Наконец, вы жестко запрограммировали в MyClass, что ему нужно ввести пять экземпляров.

Редко, что вам нужно будет ввести Provider в объект. Обычно, когда я это делаю, это потому, что область действия текущего объекта означает, что он будет более долговечным, чем область зависимого объекта (например, @Singleton, которому нужен доступ к объекту @RequestScoped).

Вместо ввода Provider вы можете ввести List<InjectedObject> в конструктор и создать метод поставщика в модуле Guice:

@Provides
MyClass prividesMyClass(Provider<InjectedObject> injectedObjectProvider) {
  List<InjectedObject> objects = new ArrayList<InjectedObject>();
  for (int i = 0; i < 5; i++) {
   objects.add(injectedObjectProvider.get());
  }
  return new MyClass(objects);
}

(вы могли бы, конечно, связываться с помощью TypeLiteral)

Почему это лучше? Несмотря на то, что вы все еще жестко кодируете пять объектов в этом коде, он не жестко закодирован в MyClass, поэтому клиенты MyClass (включая тесты для самого MyClass) могут выбрать для создания объекта в разных пути.

Если жесткое кодирование этих знаний в модуле Guice не является хорошей идеей, вы можете вместо этого создать интерфейс с более конкретным контрактом, чем Provider

public interface InjectedObjectRepository {
  List<InjectedObject> getInjectedObjects();
}

Даже если вы решите, что хотите, чтобы MyClass отвечал за то, как узнать, сколько экземпляров для создания, вы можете создать интерфейс (возможно, с именем InjectedObjectSupplier), чтобы вы могли явно указывать, что каждый раз вы ожидаете уникальный экземпляр.

Ответ 4

Добавление этого ответа, чтобы люди знали, как вводить массив, так как это первое при поиске в google. Я считаю, что любой из них будет работать:

В модуле configure: bind(InjectedObject[].class).toInstance(someArray); или как метод провайдера:

@Provides
InjectedObject[] getInjectedObject(@Inject Provider<InjectedObject> provider) {
  InjectedObject[] objects = new InjectedObject[5];
  for (int i = 0; i < objects.length; i++) {
    objects[i] = provider.get();
  }
}