Введение в Dagger 2

Введение в Dagger 2

1. Вступление

В этом руководстве мы рассмотрим Dagger 2 - быстрый и легкий фреймворк для внедрения зависимостей.

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

2. Внедрение зависимости

Напоминаем, чтоDependency Injection - это конкретное приложение более общего принципа инверсии управления, в котором выполнение программы контролируется самой программой.

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

И разные фреймворки внедряют внедрение зависимостей по-разному. В частности, одним из наиболее заметных из этих различий является то, происходит ли инъекция во время выполнения или во время компиляции.

DI времени выполнения обычно основан на отражении, которое проще в использовании, но медленнее во время выполнения. Примерa run-time DI framework is Spring.

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

Кинжал 2 попадает в эту категорию.

3. Maven/Gradle Configuration

Чтобы использовать Dagger в проекте, нам нужно добавитьthe dagger dependency к нашемуpom.xml:


    com.google.dagger
    dagger
    2.16

Кроме того, нам также понадобитсяinclude the Dagger compiler, используемый для преобразования наших аннотированных классов в код, используемый для инъекций:


    org.apache.maven.plugins
    maven-compiler-plugin
    3.6.1
    
         
              
                  com.google.dagger
                  dagger-compiler
                  2.16
              
         
    

В этой конфигурации Maven выведет сгенерированный код вtarget/generated-sources/annotations.

По этой причинеwe likely need to further configure our IDE, если мы хотим использовать любую из его функций завершения кода. Некоторые IDE имеют прямую поддержку для процессоров аннотаций, в то время как другим может понадобиться добавить этот каталог в путь сборки.

В качестве альтернативы, если мы используем Android с Gradle, мы можем включить обе зависимости:

compile 'com.google.dagger:dagger:2.16'
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'

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

4. Реализация

В нашем примере мы попытаемся построить автомобиль, введя в него компоненты.

ТеперьDagger uses the standard JSR-330 annotations во многих местах, одним из которых является@Inject.

Мы можем добавить аннотации к полям или конструктору. Но, посколькуDagger doesn’t support injection on private fields, мы перейдем к внедрению конструктора, чтобы сохранить инкапсуляцию:

public class Car {

    private Engine engine;
    private Brand brand;

    @Inject
    public Car(Engine engine, Brand brand) {
        this.engine = engine;
        this.brand = brand;
    }

    // getters and setters

}

Затем мы реализуем код для выполнения инъекции. В частности, мы создадим:

  • module, который представляет собой класс, который предоставляет или строит зависимости объектов, и

  • acomponent, который является интерфейсом, используемым для генерации инжектора

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

Посмотрим, как их реализовать.

4.1. модуль

Чтобы создать модуль,we need to annotate the class with the @Module annotation. Эта аннотация указывает, что класс может сделать зависимости доступными для контейнера:

@Module
public class VehiclesModule {
}

Тогдаwe need to add the @Provides annotation on methods that construct our dependencies:

@Module
public class VehiclesModule {
    @Provides
    public Engine provideEngine() {
        return new Engine();
    }

    @Provides
    @Singleton
    public Brand provideBrand() {
        return new Brand("example");
    }
}

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

4.2. Составная часть

Двигаясь дальше, мы собираемся создать наш компонентный интерфейс. Это класс, который будет генерировать экземпляры Car, внедряя зависимости, предоставляемыеVehiclesModule.

Проще говоря, нам нужна подпись метода, которая возвращаетCar иwe need to mark the class with the @Component annotation:

@Singleton
@Component(modules = VehiclesModule.class)
public interface VehiclesComponent {
    Car buildCar();
}

Обратите внимание, как мы передали наш класс модуля в качестве аргумента аннотации@Component. If we didn’t do that, Dagger wouldn’t know how to build the car’s dependencies.

Кроме того, поскольку наш модуль предоставляет одноэлементный объект, мы должны предоставить такую ​​же область видимости нашему компоненту, потому чтоDagger doesn’t allow for unscoped components to refer to scoped bindings.

4.3. Код клиента

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

После этого мы найдем реализацию нашего компонента с тем же именем, что и интерфейс, только с префиксом «Dagger»:

@Test
public void givenGeneratedComponent_whenBuildingCar_thenDependenciesInjected() {
    VehiclesComponent component = DaggerVehiclesComponent.create();

    Car carOne = component.buildCar();
    Car carTwo = component.buildCar();

    Assert.assertNotNull(carOne);
    Assert.assertNotNull(carTwo);
    Assert.assertNotNull(carOne.getEngine());
    Assert.assertNotNull(carTwo.getEngine());
    Assert.assertNotNull(carOne.getBrand());
    Assert.assertNotNull(carTwo.getBrand());
    Assert.assertNotEquals(carOne.getEngine(), carTwo.getEngine());
    Assert.assertEquals(carOne.getBrand(), carTwo.getBrand());
}

5. Весенние аналогии

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

Аннотация@Module в Dagger сообщает контейнеру о классе очень похожим образом, как и любые стереотипные аннотации Spring (например,@Service,@Controller…). Точно так же@Provides и@Component почти эквивалентны@Bean и@Lookup Spring соответственно.

Spring также имеет аннотацию@Scope, коррелирующую с@Singleton, хотя обратите внимание на еще одно отличие в том, что Spring по умолчанию принимает одноэлементную область видимости, в то время как Dagger по умолчанию использует то, что разработчики Spring могут называть областью прототипа, вызывая метод поставщика каждый раз, когда требуется зависимость.

6. Заключение

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

Как всегда доступен весь код статьиover on GitHub.