Inleiding tot Dagger 2

1. Inleiding

In deze tutorial zullen we Dagger 2 bekijken - een snel en lichtgewicht framework voor afhankelijkheidsinjectie.

Het framework is beschikbaar voor zowel Java als Android, maar de hoge prestaties die zijn afgeleid van compilatietijdinjectie, maken het een toonaangevende oplossing voor de laatste.

2. Afhankelijkheidsinjectie

Ter herinnering, Dependency Injection is een concrete toepassing van het meer generieke Inversion of Control-principe waarbij de stroom van het programma wordt gecontroleerd door het programma zelf.

Het wordt geïmplementeerd via een externe component die instanties biedt van objecten (of afhankelijkheden) die andere objecten nodig hebben.

En verschillende frameworks implementeren afhankelijkheidsinjectie op verschillende manieren. Een van de meest opvallende van deze verschillen is met name of de injectie tijdens runtime of tijdens compilatie plaatsvindt.

Runtime DI is meestal gebaseerd op reflectie, die eenvoudiger te gebruiken is, maar langzamer tijdens runtime. Een voorbeeld van een run-time DI-framework is Spring.

Compilatietijd DI daarentegen is gebaseerd op het genereren van code. Dit betekent dat alle zware bewerkingen worden uitgevoerd tijdens het compileren. Compilatietijd DI voegt complexiteit toe, maar presteert over het algemeen sneller.

Dagger 2 valt in deze categorie.

3. Maven / Gradle-configuratie

Om Dagger in een project te gebruiken, moeten we de dolk afhankelijkheid van onze pom.xml:

 com.google.dagger dagger 2.16 

Verder moeten we ook de Dagger-compiler opnemen die wordt gebruikt om onze geannoteerde klassen om te zetten in de code die wordt gebruikt voor de injecties:

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

Met deze configuratie zal Maven de gegenereerde code uitvoeren naar doel / gegenereerde-bronnen / annotaties.

Om deze reden, we moeten waarschijnlijk onze IDE verder configureren als we een van de functies voor het aanvullen van code willen gebruiken. Sommige IDE's hebben directe ondersteuning voor annotatieprocessors, terwijl andere mogelijk van ons nodig hebben om deze map aan het buildpad toe te voegen.

Als alternatief, als we Android met Gradle gebruiken, kunnen we beide afhankelijkheden opnemen:

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

Nu we Dagger in ons project hebben, gaan we een voorbeeldtoepassing maken om te zien hoe het werkt.

4. Implementatie

Voor ons voorbeeld zullen we proberen een auto te bouwen door de componenten ervan te injecteren.

Nu, Dagger gebruikt de standaard JSR-330-annotaties op veel plaatsen, één wezen @Injecteren.

We kunnen de annotaties toevoegen aan velden of de constructor. Maar sinds Dagger ondersteunt geen injectie op privévelden, we gaan voor constructor-injectie om inkapseling te behouden:

openbare klasse auto {particuliere motor; eigen merk; @Inject openbare auto (motormotor, merkmerk) {this.engine = engine; this.brand = merk; } // getters en setters}

Vervolgens implementeren we de code om de injectie uit te voeren. Meer specifiek zullen we creëren:

  • een module, wat een klasse is die de afhankelijkheden van de objecten levert of bouwt, en
  • een component, een interface die wordt gebruikt om de injector te genereren

Complexe projecten kunnen meerdere modules en componenten bevatten, maar aangezien we te maken hebben met een heel eenvoudig programma, is één van elk voldoende.

Laten we eens kijken hoe we ze kunnen implementeren.

4.1. Module

Om een ​​module te maken, we moeten de klasse annoteren met de @Module annotatie. Deze annotatie geeft aan dat de klasse afhankelijkheden beschikbaar kan maken voor de container:

@Module openbare klasse VehiclesModule {}

Dan, we moeten de @Biedt annotatie op methoden die onze afhankelijkheden construeren:

@Module openbare klasse VehiclesModule {@ Biedt openbare Engine offerEngine () {retourneer nieuwe Engine (); } @Provides @Singleton public Brand offerBrand () {retourneer nieuw merk ("Baeldung"); }}

Merk ook op dat we het bereik van een bepaalde afhankelijkheid kunnen configureren. In dit geval geven we de singleton-scope aan onze Merk zodat alle auto-exemplaren hetzelfde merkobject delen.

4.2. Component

Verderop gaan we onze componentinterface maken. Dit is de klasse die auto-instanties zal genereren, waarbij afhankelijkheden worden geïnjecteerd die worden geboden door VoertuigenModule.

Simpel gezegd, we hebben een methodehandtekening nodig die een retourneert Auto en we moeten de klasse markeren met de @Component annotatie:

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

Merk op hoe we onze moduleklasse als argument hebben doorgegeven aan de @Component annotatie. Als we dat niet zouden doen, zou Dagger niet weten hoe hij de afhankelijkheden van de auto moest bouwen.

Omdat onze module een singleton-object biedt, moeten we dezelfde scope aan onze component geven omdat Dagger staat niet toe dat niet-scoped-componenten verwijzen naar scoped-bindingen.

4.3. Klantcode

Eindelijk kunnen we rennen mvn compileren om de annotatieprocessors te activeren en de injectorcode te genereren.

Daarna vinden we onze componentimplementatie met dezelfde naam als de interface, alleen voorafgegaan door 'Dolk“:

@Test openbare leegte gegevenGeneratedComponent_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. Lente-analogieën

Degenen die bekend zijn met Spring hebben misschien enkele parallellen opgemerkt tussen de twee kaders.

Dagger's @Module annotatie maakt de container op een vergelijkbare manier bewust van een klasse als alle stereotype annotaties van Spring (bijvoorbeeld @Onderhoud, @Controller…). Hetzelfde, @Biedt en @Component zijn bijna gelijk aan Spring's @Boon en @Opzoeken respectievelijk.

De lente heeft ook zijn @Scope annotatie, gerelateerd aan @SingletonMerk echter op dat hier al een ander verschil is, namelijk dat Spring standaard een singleton-scope aanneemt, terwijl Dagger standaard gaat naar wat Spring-ontwikkelaars het prototype-bereik zouden kunnen noemen, waarbij de providermethode elke keer dat een afhankelijkheid vereist is, wordt aangeroepen.

6. Conclusie

In dit artikel hebben we aan de hand van een eenvoudig voorbeeld besproken hoe Dagger 2 moet worden ingesteld en gebruikt. We hebben ook gekeken naar de verschillen tussen runtime- en compilatietijdinjectie.

Zoals altijd is alle code in het artikel beschikbaar op GitHub.