Введение в 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.