Úvod do inverzie riadenia a vstrekovania závislostí pomocou pružiny

1. Prehľad

V tomto článku si predstavíme pojmy IoC (inverzia riadenia) a DI (závislosť vstrekovania) a potom sa pozrieme na to, ako sa tieto implementujú v jarnom rámci.

2. Čo je inverzia kontroly?

Inverzia riadenia je princíp v softvérovom inžinierstve, pri ktorom sa kontrola objektov alebo častí programu prenáša do kontajnera alebo rámca. Najčastejšie sa používa v kontexte objektovo orientovaného programovania.

Na rozdiel od tradičného programovania, pri ktorom náš vlastný kód umožňuje hovory do knižnice, umožňuje IoC rámci prevziať kontrolu nad tokom programu a uskutočňovať hovory s našim vlastným kódom. Aby to bolo možné, rámce používajú abstrakcie a zabudované ďalšie správanie. Ak chceme pridať svoje vlastné správanie, musíme rozšíriť triedy rámca alebo doplniť naše vlastné triedy.

Výhody tejto architektúry sú:

  • oddelenie vykonania úlohy od jej vykonania
  • čo uľahčuje prepínanie medzi rôznymi implementáciami
  • väčšia modularita programu
  • väčšie uľahčenie testovania programu izolovaním komponentu alebo zosmiešňovaním jeho závislostí a umožnením komponentom komunikovať prostredníctvom zmlúv

Inverziu kontroly je možné dosiahnuť pomocou rôznych mechanizmov, ako sú napríklad: návrhový vzor stratégie, vzor vyhľadávača služieb, vzor továrne a injekcia závislostí (DI).

Ďalej sa pozrieme na DI.

3. Čo je injekcia závislostí?

Vkladanie závislostí je vzor, ​​prostredníctvom ktorého sa má implementovať IoC, pričom invertovanou kontrolou je nastavenie závislostí objektu.

Spájanie objektov s inými objektmi alebo „vstrekovanie“ predmetov do iných objektov sa deje skôr pomocou asemblerov než samotných objektov.

Tu je príklad, ako by ste vytvorili závislosť objektu v tradičnom programovaní:

public class Store {položka súkromnej položky; public Store () {item = new ItemImpl1 (); }}

Vo vyššie uvedenom príklade musíme inštanciu implementácie Položka rozhranie v rámci Uložiť samotná trieda.

Použitím DI môžeme príklad prepísať bez špecifikácie implementácie Položka že chceme:

public class Store {položka súkromnej položky; verejný obchod (položka položky) {this.item = item; }}

V ďalších častiach uvidíme, ako môžeme zabezpečiť implementáciu Položka prostredníctvom metadát.

IoC aj DI sú jednoduché koncepty, ale majú hlboké dôsledky do spôsobu, akým štruktúrujeme naše systémy, takže stojí za to im dobre rozumieť.

4. Jarný kontajner IoC

Kontajner IoC je bežnou charakteristikou rámcov, ktoré implementujú IoC.

V jarnom rámci je kontajner IoC reprezentovaný rozhraním ApplicationContext. Nádoba Spring je zodpovedná za vytvorenie inštancie, konfiguráciu a zostavenie objektov známych ako fazuľa, ako aj správu ich životného cyklu.

Jarný rámec poskytuje niekoľko implementácií ApplicationContext rozhranie - ClassPathXmlApplicationContext a FileSystemXmlApplicationContext - pre samostatné aplikácie a - WebApplicationContext pre webové aplikácie.

Na zostavenie fazule používa kontajner konfiguračné metadáta, ktoré môžu mať formu konfigurácie XML alebo anotácií.

Tu je jeden spôsob, ako ručne vytvoriť inštanciu kontajnera:

ApplicationContext context = nový ClassPathXmlApplicationContext ("applicationContext.xml");

Ak chcete nastaviť položka atribútu v príklade vyššie, môžeme použiť metadáta. Potom kontajner načíta tieto metadáta a použije ich na zostavenie fazule za behu.

Vkladanie závislostí na jar je možné vykonať pomocou konštruktorov, nastavovačov alebo polí.

5. Vkladanie závislostí na základe konštruktéra

V prípade injektovania závislosti na základe konštruktora kontajner vyvolá konštruktor s argumentmi, z ktorých každý predstavuje závislosť, ktorú chceme nastaviť.

Jar rieši každý argument primárne podľa typu, za ktorým nasleduje názov atribútu a index pre disambiguáciu. Pozrime sa na konfiguráciu fazule a jej závislostí pomocou anotácií:

@ Konfigurácia verejná trieda AppConfig {@Bean public Položka item1 () {vrátiť nový ItemImpl1 (); } @Bean public Store store () {return new Store (item1 ()); }}

The @ Konfigurácia anotácia naznačuje, že trieda je zdrojom definícií fazule. Môžeme ho tiež pridať do viacerých konfiguračných tried.

The @Bean anotácia sa používa pri metóde na definovanie fazule. Ak nezadáme vlastný názov, predvolený názov fazule bude názov metódy.

Pre fazuľu s predvolenou hodnotou singleton scope, Spring najskôr skontroluje, či inštancia fazule v pamäti už existuje, a vytvorí iba novú, ak nie. Ak používame prototyp scope, kontajner vráti novú inštanciu fazule pre každé volanie metódy.

Ďalším spôsobom, ako vytvoriť konfiguráciu fazule, je konfigurácia XML:

6. Injekcia závislostí na základe nastavovača

Pre DI na základe nastavovača bude kontajner volať metódy nastavovača našej triedy po vyvolaní konštruktora bez argumentu alebo statickej továrenskej metódy bez argumentu na vytvorenie inštancie fazule. Vytvorme túto konfiguráciu pomocou anotácií:

@Bean public Store store () {Store store = new Store (); store.setItem (item1 ()); návratný obchod; }

Pre rovnakú konfiguráciu fazule môžeme použiť aj XML:

Pre tú istú fazuľu je možné kombinovať injektory založené na konštruktoroch a nastavovačoch. Jarná dokumentácia odporúča použiť injektovanie založené na konštruktore pre povinné závislosti a injektor založené na nastavovači pre voliteľné.

7. Terénne Injekcia závislostí

V prípade terénneho DI môžeme závislosti vložiť ich označením pomocou @Autowired anotácia:

public class Store {@Autowired private item item; }

Pri výstavbe Uložiť objekt, ak neexistuje žiadny konštruktor alebo nastavovacia metóda na vloženie súboru Položka fazuľa, nádoba použije na vstreknutie odraz Položka do Uložiť.

To môžeme dosiahnuť aj pomocou konfigurácie XML.

Tento prístup môže vyzerať jednoduchšie a čistejšie, ale neodporúča sa ho používať, pretože má niekoľko nevýhod, ako napríklad:

  • Táto metóda využíva reflexiu na vloženie závislostí, čo je nákladnejšie ako injekcia založená na konštruktore alebo setteri
  • Pomocou tohto prístupu je naozaj ľahké neustále pridávať viac závislostí. Ak by ste používali vkladanie konštruktora s viacerými argumentmi, prinútili by sme nás myslieť si, že trieda robí viac ako jednu vec, ktorá môže porušiť princíp jednej zodpovednosti.

Viac informácií na @Autowired anotáciu nájdete v článku Wiring In Spring.

8. Závislosti automatického zapojenia

Zapojenie umožňuje kontajneru Spring automaticky vyriešiť závislosti medzi spolupracujúcimi fazuľami kontrolou fazúľ, ktoré boli definované.

Existujú štyri režimy automatického zapojenia fazule pomocou konfigurácie XML:

  • č: predvolená hodnota - to znamená, že sa pre fazuľu nepoužíva žiadne automatické zapojenie a musíme výslovne pomenovať závislosti
  • podľa názvu: automatické zapojenie sa vykonáva na základe názvu nehnuteľnosti, preto bude Spring hľadať fazuľu s rovnakým názvom ako vlastnosť, ktorú je potrebné nastaviť
  • podľa typu: podobne ako podľa názvu autowiring, len na základe typu nehnuteľnosti. To znamená, že Spring bude hľadať fazuľu s rovnakým typom vlastnosti, ktorú chcete nastaviť. Ak existuje viac ako jeden fazuľa tohto typu, rámec vyvolá výnimku.
  • konštruktér: automatické zapojenie sa vykonáva na základe argumentov konštruktora, čo znamená, že Spring bude hľadať fazuľu rovnakého typu ako argumenty konštruktora

Napríklad poďme autowire položka1 fazuľa definovaná vyššie podľa typu do obchod fazuľa:

@Bean (autowire = Autowire.BY_TYPE) verejná trieda Store {položka súkromnej položky; public setItem (položka položky) {this.item = položka; }}

Fazuľu môžeme injekčne podať aj pomocou @Autowired anotácia pre automatické zapojenie podľa typu:

public class Store {@Autowired private item item; }

Ak existuje viac ako jedna fazuľa rovnakého typu, môžeme použiť @Qualifier anotácia odkazujúca na fazuľu podľa názvu:

public class Store {@Autowired @Qualifier ("item1") súkromná položka položky; }

Teraz poďme autowire fazuľa podľa typu prostredníctvom konfigurácie XML:

Ďalej si vpichneme fazuľu s menom položka do položka majetok obchod fazuľa podľa názvu prostredníctvom XML:

Autowiring môžeme tiež prepísať explicitným definovaním závislostí prostredníctvom argumentov alebo setterov konštruktora.

9. Lenivé inicializované fazule

V predvolenom nastavení kontajner vytvára a konfiguruje všetky singletonové fazule počas inicializácie. Ak sa tomu chcete vyhnúť, môžete použiť lenivý-init atribút s hodnotou pravda na konfiguráciu fazule:

V dôsledku toho položka1 bean sa inicializuje, až keď je to prvýkrát požadované, a nie pri štarte. Výhodou je rýchlejšia doba inicializácie, ale kompromisom je, že chyby konfigurácie sa môžu zistiť až po vyžiadaní objektu bean, čo môže byť niekoľko hodín alebo dokonca dní po tom, čo už bola aplikácia spustená.

10. Záver

V tomto článku sme predstavili pojmy inverzie riadenia a vkladania závislostí a ich príkladom sme boli v rámci Spring.

Viac sa o týchto konceptoch dočítate v článkoch Martina Fowlera:

  • Inverzia kontrolných kontajnerov a vzor injekcie závislostí.
  • Inverzia kontroly

A o jarných implementáciách IoC a DI sa môžete dozvedieť viac v referenčnej dokumentácii jarného rámca.