Рубрики
Без рубрики

Введение в Кинжал 2

Узнайте о внедрении зависимостей во время компиляции с помощью Dagger 2 и о том, как он сравнивается с Spring.

Автор оригинала: Donato Rimenti.

1. введение

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

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

2. Инъекция Зависимостей

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

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

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

Время выполнения DI обычно основано на отражении, которое проще в использовании, но медленнее во время выполнения. Примером фреймворка DI времени выполнения является Spring .

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

Кинжал 2 относится к этой категории.

3. Конфигурация Maven/Gradle

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


    com.google.dagger
    dagger
    2.16

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


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

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

По этой причине нам, вероятно, потребуется дополнительно настроить нашу ИДЕЮ , если мы хотим использовать какую-либо из ее функций завершения кода. Некоторые IDE имеют прямую поддержку процессоров аннотаций, в то время как другие могут нуждаться в том, чтобы мы добавили этот каталог в путь сборки.

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

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

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

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

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

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

Мы можем добавить аннотации к полям или конструктору. Но, поскольку Dagger не поддерживает инъекцию в частные поля , мы пойдем на инъекцию конструктора, чтобы сохранить инкапсуляцию:

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

}

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

  • a module , который является классом, обеспечивающим или создающим зависимости объектов, и
  • a component , который является интерфейсом, используемым для генерации инжектора

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

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

4.1. Модуль

Чтобы создать модуль, нам нужно аннотировать класс с помощью @Module annotation . Эта аннотация указывает на то, что класс может сделать зависимости доступными для контейнера:

@Module
public class VehiclesModule {
}

Затем нам нужно добавить @Provides аннотацию к методам, которые строят наши зависимости :

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

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

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

4.2. Компонент

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

Проще говоря, нам нужна сигнатура метода, которая возвращает a Car и нам нужно пометить класс @Component аннотацией :

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

Обратите внимание, как мы передали наш класс модуля в качестве аргумента аннотации @Component . Если бы мы этого не сделали, Даггер не знал бы, как построить зависимости автомобиля.

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

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, возможно, заметили некоторые параллели между этими двумя фреймворками.

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

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

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

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

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