Einführung in Dolch 2

1. Einleitung

In diesem Tutorial werfen wir einen Blick auf Dagger 2 - ein schnelles und leichtes Framework für die Abhängigkeitsinjektion.

Das Framework ist sowohl für Java als auch für Android verfügbar, aber die hohe Leistung, die sich aus der Injektion zur Kompilierungszeit ergibt, macht es zu einer führenden Lösung für letztere.

2. Abhängigkeitsinjektion

Zur Erinnerung: Dependency Injection ist eine konkrete Anwendung des allgemeineren Inversion of Control-Prinzips, bei dem der Programmfluss vom Programm selbst gesteuert wird.

Es wird über eine externe Komponente implementiert, die Instanzen von Objekten (oder Abhängigkeiten) bereitstellt, die von anderen Objekten benötigt werden.

Verschiedene Frameworks implementieren die Abhängigkeitsinjektion auf unterschiedliche Weise. Einer der bemerkenswertesten dieser Unterschiede ist insbesondere, ob die Injektion zur Laufzeit oder zur Kompilierungszeit erfolgt.

Laufzeit-DI basiert normalerweise auf Reflexion, die einfacher zu verwenden ist, aber zur Laufzeit langsamer. Ein Beispiel für ein Laufzeit-DI-Framework ist Spring.

Die Kompilierungszeit DI basiert dagegen auf der Codegenerierung. Dies bedeutet, dass alle Schwergewichtsoperationen während der Kompilierung ausgeführt werden. DI zur Kompilierungszeit erhöht die Komplexität, führt jedoch im Allgemeinen zu einer schnelleren Leistung.

Dolch 2 fällt in diese Kategorie.

3. Maven / Gradle-Konfiguration

Um Dagger in einem Projekt verwenden zu können, müssen wir die Dolchabhängigkeit zu unserer pom.xml hinzufügen :

 com.google.dagger dagger 2.16 

Darüber hinaus müssen wir auch den Dagger-Compiler einbinden, mit dem unsere mit Anmerkungen versehenen Klassen in den für die Injektionen verwendeten Code konvertiert werden:

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

Mit dieser Konfiguration gibt Maven den generierten Code in Ziel / Generierte-Quellen / Anmerkungen aus .

Aus diesem Grund müssen wir unsere IDE wahrscheinlich weiter konfigurieren, wenn wir eine der Funktionen zur Code-Vervollständigung verwenden möchten. Einige IDEs unterstützen Annotationsprozessoren direkt, während andere dieses Verzeichnis möglicherweise zum Erstellungspfad hinzufügen müssen.

Wenn Sie Android mit Gradle verwenden, können Sie alternativ beide Abhängigkeiten einbeziehen:

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

Nachdem wir Dagger in unserem Projekt haben, erstellen wir eine Beispielanwendung, um zu sehen, wie es funktioniert.

4. Implementierung

In unserem Beispiel werden wir versuchen, ein Auto zu bauen, indem wir seine Komponenten einspritzen.

Jetzt verwendet Dagger an vielen Stellen die Standard-JSR-330-Anmerkungen , eine davon ist @Inject.

Wir können die Anmerkungen zu Feldern oder dem Konstruktor hinzufügen. Da Dagger die Injektion auf privaten Feldern nicht unterstützt , werden wir uns für die Konstruktorinjektion entscheiden, um die Kapselung zu erhalten:

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 }

Als nächstes implementieren wir den Code, um die Injektion durchzuführen. Genauer gesagt erstellen wir:

  • ein Modul , das eine Klasse ist, die die Abhängigkeiten der Objekte bereitstellt oder erstellt, und
  • Eine Komponente , die eine Schnittstelle zur Erzeugung des Injektors ist

Komplexe Projekte können mehrere Module und Komponenten enthalten, aber da es sich um ein sehr einfaches Programm handelt, reicht jeweils eines aus.

Mal sehen, wie man sie implementiert.

4.1. Modul

Um ein Modul zu erstellen, müssen wir die Klasse mit der Annotation @Module versehen . Diese Anmerkung gibt an, dass die Klasse dem Container Abhängigkeiten zur Verfügung stellen kann:

@Module public class VehiclesModule { }

Dann müssen wir die Annotation @Provides zu Methoden hinzufügen, die unsere Abhängigkeiten erstellen :

@Module public class VehiclesModule { @Provides public Engine provideEngine() { return new Engine(); } @Provides @Singleton public Brand provideBrand() { return new Brand("Baeldung"); } }

Beachten Sie außerdem, dass wir den Umfang einer bestimmten Abhängigkeit konfigurieren können. In diesem Fall geben wir die Singleton Rahmen unserer Marke Instanz so alle Auto - Instanzen die gleiche Marke Objekt teilen.

4.2. Komponente

Im weiteren Verlauf werden wir unsere Komponentenschnittstelle erstellen . Dies ist die Klasse, die Car-Instanzen generiert und Abhängigkeiten einfügt, die von VehiclesModule bereitgestellt werden .

Simply put, we need a method signature that returns a Car and we need to mark the class with the @Component annotation:

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

Notice how we passed our module class as an argument to the @Component annotation. If we didn't do that, Dagger wouldn't know how to build the car's dependencies.

Also, since our module provides a singleton object, we must give the same scope to our component because Dagger doesn't allow for unscoped components to refer to scoped bindings.

4.3. Client Code

Finally, we can run mvn compile in order to trigger the annotation processors and generate the injector code.

After that, we'll find our component implementation with the same name as the interface, just prefixed with “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 Analogies

Those familiar with Spring may have noticed some parallels between the two frameworks.

Dagger's @Module annotation makes the container aware of a class in a very similar fashion as any of Spring's stereotype annotations (for example, @Service, @Controller…). Likewise, @Provides and @Component are almost equivalent to Spring's @Bean and @Lookup respectively.

Spring hat auch seine @ Scope- Annotation, die mit @ Singleton korreliert. Beachten Sie hier jedoch bereits einen weiteren Unterschied darin, dass Spring standardmäßig einen Singleton-Bereich annimmt, während Dagger standardmäßig den von Spring-Entwicklern als Prototyp-Bereich bezeichneten Bereich verwendet und jedes Mal die Provider-Methode aufruft Abhängigkeit ist erforderlich.

6. Fazit

In diesem Artikel wurde anhand eines grundlegenden Beispiels erläutert, wie Dagger 2 eingerichtet und verwendet wird. Wir haben auch die Unterschiede zwischen Laufzeit- und Kompilierungsinjektion berücksichtigt.

Wie immer ist der gesamte Code im Artikel auf GitHub verfügbar.