Inleiding tot inversie van controle en afhankelijkheidsinjectie met lente

1. Overzicht

In dit artikel introduceren we de concepten IoC (Inversion of Control) en DI (Dependency Injection), en bekijken we hoe deze worden geïmplementeerd in het Spring-framework.

2. Wat is omkering van controle?

Inversion of Control is een principe in software engineering waarmee de besturing van objecten of delen van een programma wordt overgedragen naar een container of raamwerk. Het wordt meestal gebruikt in de context van objectgeoriënteerd programmeren.

In tegenstelling tot traditioneel programmeren, waarbij onze aangepaste code een bibliotheek aanroept, stelt IoC een raamwerk in staat om de stroom van een programma over te nemen en onze aangepaste code aan te roepen. Om dit mogelijk te maken, gebruiken frameworks abstracties met ingebouwd aanvullend gedrag. Als we ons eigen gedrag willen toevoegen, moeten we de klassen van het framework uitbreiden of onze eigen klassen plug-in.

De voordelen van deze architectuur zijn:

  • het loskoppelen van de uitvoering van een taak en de uitvoering ervan
  • waardoor het gemakkelijker wordt om tussen verschillende implementaties te schakelen
  • grotere modulariteit van een programma
  • meer gemak bij het testen van een programma door een component te isoleren of de afhankelijkheden ervan te bespotten en componenten te laten communiceren via contracten

Inversion of Control kan worden bereikt via verschillende mechanismen, zoals: Strategieontwerppatroon, Service Locator-patroon, Fabriekspatroon en Dependency Injection (DI).

We gaan nu naar DI kijken.

3. Wat is afhankelijkheidsinjectie?

Dependency injection is een patroon om IoC te implementeren, waarbij de controle die wordt omgekeerd de instelling is van de afhankelijkheden van het object.

Het verbinden van objecten met andere objecten, of het 'injecteren' van objecten in andere objecten, wordt gedaan door een assembler in plaats van door de objecten zelf.

Hier ziet u hoe u een objectafhankelijkheid creëert in traditionele programmering:

openbare klasse Store {privé-itemitem; openbare winkel () {item = new ItemImpl1 (); }}

In het bovenstaande voorbeeld moeten we een implementatie van het Item interface binnen het Winkel klasse zelf.

Door DI te gebruiken, kunnen we het voorbeeld herschrijven zonder de implementatie van Item dat we willen:

openbare klasse Store {privé-itemitem; openbare winkel (itemitem) {this.item = item; }}

In de volgende secties zullen we zien hoe we de implementatie van Item via metadata.

Zowel IoC als DI zijn eenvoudige concepten, maar hebben diepe implicaties voor de manier waarop we onze systemen structureren, dus ze zijn de moeite waard om goed te begrijpen.

4. De Spring IoC-container

Een IoC-container is een algemeen kenmerk van frameworks die IoC implementeren.

In het Spring-framework wordt de IoC-container vertegenwoordigd door de interface ApplicationContext. De Spring-container is verantwoordelijk voor het instantiëren, configureren en assembleren van objecten die bekend staan ​​als bonen, evenals het beheer van hun levenscyclus.

Het Spring-framework biedt verschillende implementaties van het ApplicationContext koppel - ClassPathXmlApplicationContext en FileSystemXmlApplicationContext voor zelfstandige toepassingen, en WebApplicationContext voor webapplicaties.

Om bonen samen te stellen, gebruikt de container configuratiemetagegevens, die de vorm kunnen hebben van XML-configuratie of annotaties.

Hier is een manier om handmatig een container te instantiëren:

ApplicationContext context = nieuwe ClassPathXmlApplicationContext ("applicationContext.xml");

Om het item attribuut in het bovenstaande voorbeeld, kunnen we metadata gebruiken. Vervolgens leest de container deze metagegevens en gebruikt deze om bonen tijdens runtime samen te stellen.

Dependency Injection in Spring kan worden gedaan via constructeurs, setters of velden.

5. Op constructeurs gebaseerde afhankelijkheidsinjectie

In het geval van een op constructor gebaseerde afhankelijkheidsinjectie, zal de container een constructor aanroepen met argumenten die elk een afhankelijkheid vertegenwoordigen die we willen instellen.

Spring lost elk argument primair op op type, gevolgd door de naam van het attribuut en de index voor ondubbelzinnig maken. Laten we de configuratie van een bean en zijn afhankelijkheden bekijken met behulp van annotaties:

@Configuration public class AppConfig {@Bean public Item item1 () {return new ItemImpl1 (); } @Bean public Store store () {retourneer nieuwe Store (item1 ()); }}

De @Configuratie annotatie geeft aan dat de klasse een bron is van bean-definities. We kunnen het ook toevoegen aan meerdere configuratieklassen.

De @Boon annotatie wordt gebruikt bij een methode om een ​​boon te definiëren. Als we geen aangepaste naam specificeren, wordt de bean-naam standaard de naam van de methode.

Voor een boon met de standaard singleton scope controleert Spring eerst of er al een cache-instantie van de bean bestaat en maakt alleen een nieuwe als dat niet het geval is. Als we de voorlopig ontwerp scope, retourneert de container een nieuwe bean-instantie voor elke methodeaanroep.

Een andere manier om de configuratie van de bonen te maken, is via XML-configuratie:

6. Op setter gebaseerde afhankelijkheidsinjectie

Voor op setter gebaseerde DI roept de container setter-methoden van onze klasse aan, na het aanroepen van een constructor zonder argument of een statische fabrieksmethode zonder argument om de bean te instantiëren. Laten we deze configuratie maken met behulp van annotaties:

@Bean public Store store () {Store store = new Store (); store.setItem (item1 ()); retourneren winkel; }

We kunnen ook XML gebruiken voor dezelfde configuratie van bonen:

Op constructeurs gebaseerde en op setter gebaseerde soorten injectie kunnen voor dezelfde boon worden gecombineerd. De Spring-documentatie raadt het gebruik van op constructor gebaseerde injectie aan voor verplichte afhankelijkheden en op setter gebaseerde injectie voor optionele.

7. Op het veld gebaseerd Afhankelijkheidsinjectie

In het geval van Field-Based DI kunnen we de afhankelijkheden injecteren door ze te markeren met een @Autowired annotatie:

openbare klasse Store {@Autowired privéitem; }

Bij het construeren van het Winkel object, als er geen constructor- of setter-methode is om het Item boon, zal de container reflectie gebruiken om te injecteren Item in Winkel.

We kunnen dit ook bereiken door middel van XML-configuratie.

Deze aanpak ziet er misschien eenvoudiger en schoner uit, maar wordt niet aanbevolen omdat het een aantal nadelen heeft, zoals:

  • Deze methode maakt gebruik van reflectie om de afhankelijkheden te injecteren, wat duurder is dan op constructeurs gebaseerde of op setter gebaseerde injectie
  • Met deze aanpak is het heel gemakkelijk om meerdere afhankelijkheden toe te voegen. Als je constructor-injectie met meerdere argumenten zou gebruiken, zouden we hebben laten denken dat de klasse meer dan één ding doet dat in strijd kan zijn met het Single Responsibility Principle.

Meer informatie over @Autowired annotatie is te vinden in het artikel Wiring In Spring.

8. Afhankelijkheden van automatische bedrading

Door bedrading kan de Spring-container automatisch afhankelijkheden tussen samenwerkende bonen oplossen door de bonen te inspecteren die zijn gedefinieerd.

Er zijn vier manieren om een ​​bean automatisch te bedraden met behulp van een XML-configuratie:

  • Nee: de standaardwaarde - dit betekent dat er geen autowiring wordt gebruikt voor de bean en dat we de afhankelijkheden expliciet moeten noemen
  • bij naam: automatische bedrading wordt gedaan op basis van de naam van de eigenschap, daarom zoekt Spring naar een boon met dezelfde naam als de eigenschap die moet worden ingesteld
  • byType: vergelijkbaar met de bij naam automatische bedrading, alleen gebaseerd op het type woning. Dit betekent dat de lente op zoek gaat naar een boon met hetzelfde type eigenschap om in te stellen. Als er meer dan één bean van dat type is, genereert het framework een uitzondering.
  • constructeur: autowiring wordt gedaan op basis van constructorargumenten, wat betekent dat Spring zoekt naar bonen met hetzelfde type als de constructorargumenten

Laten we bijvoorbeeld het item 1 bean hierboven gedefinieerd door type in de winkel Boon:

@Bean (autowire = Autowire.BY_TYPE) openbare klasse Store {privé-itemitem; public setItem (item item) {this.item = item; }}

We kunnen ook bonen injecteren met de @Autowired annotatie voor automatische bedrading op type:

openbare klasse Store {@Autowired privéitem; }

Als er meer dan één boon van hetzelfde type is, kunnen we de @Kwalificatie annotatie om op naam naar een boon te verwijzen:

openbare klasse Store {@Autowired @Qualifier ("item1") privé-itemitem; }

Laten we nu bonen autowire op type via XML-configuratie:

Laten we vervolgens een boon met de naam injecteren item in de item eigendom van winkel bean op naam via XML:

We kunnen de autowiring ook overschrijven door afhankelijkheden expliciet te definiëren via constructorargumenten of setters.

9. Luie geïnitialiseerde bonen

Standaard maakt en configureert de container alle singletonbeans tijdens de initialisatie. Om dit te voorkomen, kunt u de lui-init attribuut met waarde waar op de bonenconfiguratie:

Als gevolg hiervan is het item 1 bean wordt alleen geïnitialiseerd wanneer dit voor het eerst wordt gevraagd, en niet bij het opstarten. Het voordeel hiervan is een snellere initialisatietijd, maar het nadeel is dat configuratiefouten pas kunnen worden ontdekt nadat de bean is aangevraagd, wat enkele uren of zelfs dagen kan duren nadat de toepassing al heeft gedraaid.

10. Conclusie

In dit artikel hebben we de concepten van inversie van controle en afhankelijkheidsinjectie gepresenteerd en geïllustreerd in het Spring-raamwerk.

U kunt meer over deze concepten lezen in de artikelen van Martin Fowler:

  • Inversie van controlecontainers en het patroon van afhankelijkheidsinjectie.
  • Omkering van controle

En u kunt meer leren over de Spring-implementaties van IoC en DI in de Spring Framework Reference Documentation.


$config[zx-auto] not found$config[zx-overlay] not found