Перейти к основному содержанию

Основы

Инъекция зависимостей, ключи, привязки#

  • Приложения состоят из компонентов, и каждый компонент имеет внутренний id под названием Key
  • Ключ состоит из типа Type и nullable qualifier Object (полезно, когда вам нужны различные реализации одного и того же Type):
public class Key<T> {    @NotNull    private final Type type;    @Nullable    private final Object qualifier;}
  • Для создания компонентов приложения могут потребоваться некоторые зависимости.
  • Dependency Injection заботится о снабжении компонентов приложения этими необходимыми объектами.
  • Для этого необходимо указать, что нужно предоставить и как использовать предоставленные объекты.
  • Поэтому, привязка имеет два соответствующих атрибута:
    • набор необходимых для создания Set<Dependency> зависимостей (POJO с ключом ``).
    • BindingCompiler который будет компилировать необходимые экземпляры
 public final class Binding<T> {     final Set<Dependency> dependencies;     final BindingCompiler<T> compiler;}
  • Binding - это как "рецепт" того, как создать экземпляр компонента:
    • зависимости показывают, какие ингредиенты следует использовать
    • компилятор знает, как приготовить их вместе
  • Теперь нам нужно что-то, что может использовать рецепт для правильного приготовления компонента, и здесь на помощь приходит Injector

Инжектор#

  • Обеспечивает все необходимые зависимости (инжектирует) для компонента рекурсивно обходя граф зависимостей в постпорядковом порядке и создавая их в первую очередь.
  • Связи по умолчанию являются синглтонами - если экземпляр был создан один раз, он не будет воссоздан с нуля снова. Если нужен для других привязок, инжектор возьмет его из кэша. Для него не нужно применять никаких дополнительных аннотаций .
  • Чтобы предоставить запрошенный ключ, инжектор *** рекурсивно создает все свои зависимости и возвращается к инжектору своего родителя области* , если привязка в его области не найдена.

Scopes#

Короче говоря - Scope дает нам "локальные синглтоны", которые живут столько же, сколько и сама область видимости. Области ActiveJ Inject немного отличаются от других DI-библиотек:

  • Внутренняя структура инжектора **** представляет собой префиксное дерево , а префикс является областью действия.
  • Идентификаторы (или префиксы) дерева являются простыми аннотациями.
  • Инжектор может войти в сферу. Это означает, что вы создаете инжектор `` , и его область видимости будет установлена на ту, в которую он входит.
  • Это может быть сделано несколько раз, так что вы можете иметь N форсунок в определенном объеме.
public class Injector { ...    final Trie<Scope, ScopeLocalData> scopeDataTree; ...
    public Injector enterScope(Scope scope) {        return new Injector(this, scopeDataTree.get(scope));    }...}

Этот пример может показать вам, как работает scopes.

Модули#

Граф зависимостей трудно создать напрямую, поэтому мы предоставляем механизмы автоматического преобразования, генерации и валидации графа с простым, но мощным DSL.

Все эти шаги предварительной обработки выполняются во время запуска путем компиляции Modules

  • Каждый модуль экспортирует привязки, которые комбинируются друг с другом. Если для какой-либо одной клавиши существует две или более привязок, они сводятся в одну привязку с помощью пользовательской функции Multibinder reduce:

    • Это простое решение позволяет тривиально реализовать мультибиндер множества/карты или любой специфический для приложения мультибиндер.
    • Если для конкретного ключа не определено ни одного Multibinder , возникает исключение.
  • Если в графе зависимостей есть недостающие зависимости, они автоматически генерируются с помощью функции BindingGenerator

    • BindingGenerators определяются пользователем и экспортируются модулями.
    • Существует неявный DefaultModule со стандартным BindingGenerator, который автоматически предоставляет необходимые зависимости, сканируя Inject аннотации необходимых классов.
    • Пользовательские модули могут также экспортировать пользовательские генераторы привязок для специальных классов
    • Вы можете отказаться от использования DefaultModule и его генераторов привязок по умолчанию .
  • Все привязки преобразуются с помощью предоставленных пользователем BindingTransformers

    • Для перехвата/изменения/обертывания предоставленных экземпляров
    • Для перехвата/изменения/обертывания зависимостей предоставленных экземпляров
  • Multibinders, BindingGenerators и BindingTransformers могут быть сделаны с помощью чистого и чрезвычайно простого функционального DSL на Java8.

  • Полученный граф зависимостей валидируется - проверяется на наличие циклических и отсутствующих зависимостей, затем компилируется в окончательное дерево областей применения и передается на Injector.

public interface Module {    Trie<Scope, Map<Key<?>, Set<Binding<?>>>> getBindings();    Map<Integer, Set<BindingTransformer<?>>> getBindingTransformers();    Map<Class<?>, Set<BindingGenerator<?>>> getBindingGenerators();    Map<Key<?>, Multibinder<?>> getMultibinders();}

Тривиально реализовать интерфейс Module вручную, но еще проще расширить AbstractModule, который поддерживает сканирование методов @Provides и DSL для создания/преобразования/генерации привязок.