Introduction à Dagger 2

Introduction à la dague 2

1. introduction

Dans ce didacticiel, nous allons examiner Dagger 2 - un framework d'injection de dépendances rapide et léger.

Le framework est disponible à la fois pour Java et Android, mais la haute performance dérivée de l'injection au moment de la compilation en fait une solution de choix pour ce dernier.

2. Injection de dépendance

Pour rappel,Dependency Injection est une application concrète du principe d'inversion de contrôle plus générique dans lequel le flux du programme est contrôlé par le programme lui-même.

Il est implémenté via un composant externe qui fournit des instances d'objets (ou de dépendances) nécessaires à d'autres objets.

Et différents frameworks implémentent l'injection de dépendance de différentes manières. L'une des plus remarquables de ces différences est notamment de savoir si l'injection a lieu à l'exécution ou à la compilation.

Les ID d'exécution sont généralement basés sur une réflexion plus simple à utiliser mais plus lente au moment de l'exécution. Un exemple dea run-time DI framework is Spring.

La compilation au moment de la compilation, par contre, est basée sur la génération de code. Cela signifie que toutes les opérations lourdes sont effectuées lors de la compilation. Les ID au moment de la compilation ajoutent de la complexité mais fonctionnent généralement plus rapidement.

Dague 2 entre dans cette catégorie.

3. Maven/Gradle Configuration

Pour utiliser Dagger dans un projet, nous devons ajouterthe dagger dependency à nospom.xml:


    com.google.dagger
    dagger
    2.16

De plus, nous aurons également besoin deinclude the Dagger compiler utilisés pour convertir nos classes annotées dans le code utilisé pour les injections:


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

Avec cette configuration, Maven sortira le code généré entarget/generated-sources/annotations.

Pour cette raison,we likely need to further configure our IDE si nous voulons utiliser l'une de ses fonctionnalités de complétion de code. Certains IDE prennent directement en charge les processeurs d'annotation, tandis que d'autres peuvent avoir besoin que nous ajoutions ce répertoire au chemin de génération.

Sinon, si nous utilisons Android avec Gradle, nous pouvons inclure les deux dépendances:

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

Maintenant que nous avons Dagger dans notre projet, créons un exemple d'application pour voir comment cela fonctionne.

4. la mise en oeuvre

Pour notre exemple, nous allons essayer de construire une voiture en injectant ses composants.

Maintenant,Dagger uses the standard JSR-330 annotations à de nombreux endroits, l'un étant@Inject.

Nous pouvons ajouter les annotations aux champs ou au constructeur. Mais, depuisDagger doesn’t support injection on private fields, nous allons opter pour l'injection de constructeur pour préserver l'encapsulation:

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

}

Ensuite, nous allons mettre en œuvre le code pour effectuer l'injection. Plus précisément, nous allons créer:

  • amodule, qui est une classe qui fournit ou construit les dépendances des objets, et

  • acomponent, qui est une interface utilisée pour générer l'injecteur

Les projets complexes peuvent contenir plusieurs modules et composants, mais comme nous avons affaire à un programme très basique, un de chacun suffit.

Voyons comment les mettre en œuvre.

4.1. Module

Pour créer un module,we need to annotate the class with the @Module annotation. Cette annotation indique que la classe peut rendre des dépendances disponibles pour le conteneur:

@Module
public class VehiclesModule {
}

Ensuite,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");
    }
}

Notez également que nous pouvons configurer la portée d'une dépendance donnée. Dans ce cas, nous donnons la portée singleton à notre instanceBrand afin que toutes les instances car partagent le même objet de marque.

4.2. Composant

En continuant, nous allons créer notre interface de composantC'est la classe qui va générer des instances Car, en injectant les dépendances fournies parVehiclesModule.

En termes simples, nous avons besoin d'une signature de méthode qui renvoie unCar etwe need to mark the class with the @Component annotation:

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

Remarquez comment nous avons passé notre classe de module comme argument à l'annotation@Component. If we didn’t do that, Dagger wouldn’t know how to build the car’s dependencies.

De plus, puisque notre module fournit un objet singleton, nous devons donner la même portée à notre composant carDagger doesn’t allow for unscoped components to refer to scoped bindings.

4.3. Code client

Enfin, nous pouvons exécutermvn compile afin de déclencher les processeurs d'annotation et générer le code de l'injecteur.

Après cela, nous trouverons notre implémentation de composant avec le même nom que l'interface, juste précédée de "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. Analogies de printemps

Ceux qui sont familiers avec Spring ont peut-être remarqué des parallèles entre les deux cadres.

L'annotation@Module de Dagger rend le conteneur conscient d'une classe d'une manière très similaire à toutes les annotations stéréotypées de Spring (par exemple,@Service,@Controller…). De même,@Provides et@Component sont presque équivalents aux@Bean et@Lookup de Spring respectivement.

Spring a également son annotation@Scope, en corrélation avec@Singleton, bien que notez ici déjà une autre différence en ce que Spring suppose une portée singleton par défaut tandis que Dagger utilise par défaut ce que les développeurs Spring pourraient appeler la portée prototype, en invoquant la méthode du fournisseur chaque fois qu'une dépendance est requise.

6. Conclusion

Dans cet article, nous avons expliqué comment configurer et utiliser Dagger 2 avec un exemple élémentaire. Nous avons également examiné les différences entre l'injection au moment de l'exécution et celle au moment de la compilation.

Comme toujours, tout le code de l'article est disponibleover on GitHub.