Guice vs tavasz - függőségi injekció

1. Bemutatkozás

Google Guice és Tavaszi két robusztus keretrendszer, amelyet a függőség-injektáláshoz használnak. Mindkét keret lefedi a függőség-injektálás összes fogalmát, de mindegyiknek megvan a maga módja annak megvalósítására.

Ebben az oktatóanyagban megvitatjuk, hogy a Guice és a Spring keretrendszer hogyan különbözik konfigurációjában és megvalósításában.

2. Maven-függőségek

Kezdjük azzal, hogy hozzáadjuk a Guice és a Spring Maven függőségeket pom.xml fájl:

 org.springframework tavaszi kontextus 5.1.4. KIADÁS com.google.inject guice 4.2.2 

Mindig elérhetjük a legújabbakat tavaszi kontextus vagy guice függőségek a Maven Central-tól.

3. Függőségi injekció konfigurálása

A függőség-injektálás egy olyan programozási technika, amelyet arra használunk, hogy osztályaink függetlenné váljanak a függőségektől.

Ebben a szakaszban számos olyan alapvető jellemzőre fogunk hivatkozni, amelyek Spring és Guice között különböznek a függőségi injekció konfigurálásának módjaiban.

3.1. Tavaszi kábelezés

Spring deklarálja a függőség-befecskendezés konfigurációit egy speciális konfigurációs osztályban. Ezt az osztályt a @ Konfiguráció annotáció. A Spring konténer ezt az osztályt használja a babdefiníciók forrásaként.

A Spring által kezelt osztályokat hívjákTavaszi bab.

A tavasz a @Autowired annotáció a függőségek automatikus bekötésére. @Autowired a Spring beépített magjegyzeteinek része. Tudjuk használni @Autowired tagváltozókról, szetter módszerekről és konstruktorokról.

A tavasz is támogatja @ Injekció. @ Injekció a Java CDI (Contexts and Dependency Injection) része, amely meghatározza a függőség-injektálás szabványát.

Tegyük fel, hogy a függőséget automatikusan egy tagváltozóhoz akarjuk kapcsolni. Egyszerűen feljegyezhetjük @Autowired:

@Component public class UserService {@Autowired private AccountService accountService; }
@Component public class AccountServiceImpl megvalósítja a AccountService {}

Másodszor, hozzunk létre egy konfigurációs osztályt, amely a bab forrásaként használható az alkalmazáskörnyezet betöltésekor:

@Configuration @ComponentScan ("com.baeldung.di.spring") nyilvános osztály SpringMainConfig {}

Vegye figyelembe, hogy mi is jegyzeteltük UserService és AccountServiceImpl val vel @Összetevő hogy babként regisztrálja őket. Ez a @ComponentScan annotáció, amely megmondja Springnek, hogy hol keressen jegyzetekkel ellátott összetevőkre.

Annak ellenére, hogy jegyzeteltük AccountServiceImpl,A tavasz feltérképezheti a AccountService mivel megvalósítja AccountService.

Ezután meg kell határoznunk egy alkalmazási kontextust a bab eléréséhez. Megjegyezzük, hogy erre az összefüggésre hivatkozunk majd minden tavaszi egységtesztünk során:

ApplicationContext context = új AnnotationConfigApplicationContext (SpringMainConfig.class);

Most futás közben lekérhetjük a AccountService például a mi UserService bab:

UserService userService = context.getBean (UserService.class); assertNotNull (userService.getAccountService ());

3.2. Guice-kötés

A Guice egy modulban nevezett speciális osztályban kezeli függőségeit. A Guice modulnak ki kell terjesztenie a AbstractModule osztály és felülírja annak Beállítás() módszer.

Guice a kötést használja a tavaszi huzalozás megfelelőjeként. Egyszerűen fogalmazva, a kötések lehetővé teszik számunkra, hogy meghatározzuk, hogyan kell a függőségeket beadni egy osztályba. A Guice-összerendeléseket modulunk deklarálja Beállítás() módszer.

Ahelyett @Autowired, Guice a @ Injekció annotáció a függőségek injektálására.

Hozzunk létre egy megfelelő Guice-példát:

public class GuiceUserService {@Inject private AccountService accountService; }

Másodszor létrehozzuk a modul osztályt, amely a kötelező definícióink forrása:

a public class GuiceModule kiterjeszti az AbstractModule {@Orride védett void configure () {bind (AccountService.class) .to (AccountServiceImpl.class); }}

Általában azt várjuk a Guice-tól, hogy minden függőségi objektumot példányosítson az alapértelmezett konstruktoraiktól, ha a Beállítás() módszer. De mivel az interfészeket nem lehet közvetlenül példányosítani, meg kell határoznunk a kötéseket megmondani Guice-nak, melyik interfészt melyik megvalósítással párosítják.

Ezután meg kell határoznunk egy Injektor felhasználásával GuiceModule hogy megszerezzük az osztályaink példányait. Csak vegyük figyelembe, hogy minden Guice tesztünk ezt fogja használni Injektor:

Injektorinjektor = Guice.createInjector (új GuiceModule ());

Végül futás közben lekérjük a GuiceUserService példány nem null értékkel accountService függőség:

GuiceUserService guiceUserService = injector.getInstance (GuiceUserService.class); assertNotNull (guiceUserService.getAccountService ());

3.3. Tavaszi @Bean Annotation

A Spring egy módszerszintű kommentárral is szolgál @Bab babot regisztrálni az osztályszintű kommentárok alternatívájaként, mint a @Összetevő. Az a visszatérési értéke @Bab az annotált módszer babként szerepel a tartályban.

Tegyük fel, hogy van egy példányunk BookServiceImpl hogy az injekcióhoz hozzáférhetővé akarjuk tenni. Használhatnánk @Bab példányunk regisztrálásához:

@Bean public BookService bookServiceGenerator () {return new BookServiceImpl (); }

És most kaphatunk egy BookService bab:

BookService bookService = context.getBean (BookService.class); assertNotNull (bookService);

3.4. Guice @ Megjegyzéseket nyújt

A Spring's megfelelőjeként @Bab kommentár, A Guice-nak van beépített annotációja @ Biztosítja hogy ugyanazt a munkát végezze. Mint @Bab, @ Biztosítja csak a módszerekre alkalmazzák.

Most hajtsuk végre az előző tavaszi bab példát a Guice-szal. Csak annyit kell tennünk, hogy hozzáadjuk a következő kódot a modul osztályunkhoz:

@ Nyilvános BookService-t biztosít bookServiceGenerator () {return new BookServiceImpl (); }

És most visszakereshetjük a BookService:

BookService bookService = injector.getInstance (BookService.class); assertNotNull (bookService);

3.5. Classpath Component szkennelés tavasszal

A tavasz biztosítja a @ComponentScan az annotáció automatikusan felismeri és példányosítja a jegyzetelt komponenseket előre definiált csomagok beolvasásával.

A @ComponentScan az annotáció megmondja Springnek, hogy mely csomagokat vizsgálják meg a jegyzetelt alkatrészek után. Ezzel együtt használják @ Konfiguráció annotáció.

3.6. Classpath Component Scanning in Guice

Tavasztól eltérően A Guice nem rendelkezik ilyen komponens-szkennelési funkcióval. De nem nehéz szimulálni. Van néhány plugin, mint Kormányzó ami behozhatja ezt a funkciót a Guice-ba.

3.7. Tárgyfelismerés tavasszal

A tavasz a tárgyakat a nevük alapján ismeri fel. A tavasz olyan objektumban tartja az objektumokat, amely nagyjából olyan, mint a Térkép. Ez azt jelenti, hogy nem lehet két azonos nevű objektumunk.

Bean ütközés miatt több azonos nevű bab egy gyakori probléma Tavaszi fejlesztők ütöttek. Vegyük például a következő babdeklarációkat:

@Configuration @Import ({SpringBeansConfig.class}) @ComponentScan ("com.baeldung.di.spring") nyilvános osztály SpringMainConfig {@Bean public BookService bookServiceGenerator () {return new BookServiceImpl (); }}
@Configuration public class SpringBeansConfig {@Bean public AudioBookService bookServiceGenerator () {return new AudioBookServiceImpl (); }}

Mint emlékszünk, már volt egy babdefiníciónk BookService ban ben SpringMainConfig osztály.

A babszem ütközésének létrehozásához itt azonos névvel kell deklarálnunk a bab módszereket. De nem megengedett, hogy két különböző, azonos névvel rendelkező módszerünk legyen egy osztályban. Ezért deklaráltuk a AudioBookService bab egy másik konfigurációs osztályban.

Nézzük át ezeket a babokat egy tesztben:

BookService bookService = context.getBean (BookService.class); assertNotNull (bookService); AudioBookService audioBookService = context.getBean (AudioBookService.class); assertNotNull (audioBookService);

Az egység teszt sikertelen lesz:

org.springframework.beans.factory.NoSuchBeanDefinitionException: Nincs elérhető „AudioBookService” típusú minősítő bab

Először Spring regisztrálta a AudioBookService bab „BookServiceGenerator” neve babkártyáján. Aztán felül kellett írnia a babdefinícióval BookService miatt a „Nem engedélyezik a névmásolatokat” jellege HashMap adatszerkezet.

Végül, leküzdhetjük ezt a kérdést, ha a bab módszer nevét egyedivé tesszük, vagy a név attribútumot mindegyik egyedi névhez @Bab.

3.8. Tárgyfelismerés Guice-ban

Tavasztól eltérően Guice-nak alapvetően van egy Térkép szerkezet. Ez azt jelenti, hogy több metaadat használata nélkül nem lehet több kötésünk ugyanahhoz a típushoz.

A Guice kötési kommentárokkal látja el a többféle kötés definiálását ugyanazon típushoz. Lássuk, mi történik, ha ugyanannak a típusnak két különböző kötése van a Guice-ban.

nyilvános osztály Személy {}

Most nyilvánítsunk két különböző kötést a Személy osztály:

bind (Személy.osztály) .toConstructor (Személy.osztály.getConstructor ()); bind (Person.class) .toProvider (new Provider () {public Person get () {Person p = new Person (); return p;}});

És itt találhatunk példát Személy osztály:

Személy személy = injector.getInstance (Személy.osztály); assertNotNull (személy);

Ez nem sikerül:

com.google.inject.CreationException: A személyhez való kötést már konfigurálták a GuiceModule.configure () oldalon

Leküzdhetjük ezt a kérdést, ha egyszerűen elvetjük az egyik kötést Személy osztály.

3.9. Opcionális függőségek tavasszal

Az opcionális függőségek azok a függőségek, amelyekre nincs szükség, ha babot öntenek vagy injektálnak.

Olyan mező esetében, amelyhez jegyzet került @Autowired, ha egy összefüggő adattípusú bab nem található a kontextusban, a Spring dobni fog NoSuchBeanDefinitionException.

Azonban, néha érdemes kihagynunk bizonyos függőségek automatikus bekapcsolását, és hagyjuk őket nullakivétel nélkül:

Most nézzük meg a következő példát:

@ Component public class BookServiceImpl implementálja a BookService {@Autowired private AuthorService authorService; }
public class AuthorServiceImpl megvalósítja AuthorService {}

Amint a fenti kódból láthatjuk, AuthorServiceImpl osztályt nem jegyezték fel komponensként. És feltételezzük, hogy a konfigurációs fájljainkban nincs babdeklarációs módszer.

Most futtassuk a következő tesztet, hogy megnézzük, mi történik:

BookService bookService = context.getBean (BookService.class); assertNotNull (bookService);

Nem meglepő, hogy a következő esetekben fog kudarcot vallani:

org.springframework.beans.factory.NoSuchBeanDefinitionException: Nincs elérhető „AuthorService” típusú minősítő bab

Meg tudjuk csinálni authorService a függőség opcionális a Java 8 használatával Választható írja be ezt a kivételt.

public class BookServiceImpl megvalósítja a BookService {@Autowired private Opcionális authorService; }

Most, a mi authorService a függőség inkább olyan tárolóhoz hasonlít, amely tartalmazhat babot vagy nem AuthorService típus. Annak ellenére, hogy nincs bab AuthorService alkalmazási környezetünkben a authorService mező továbbra is nem lesznulla üres konténer. Ennélfogva Springnek semmi oka nem lesz dobni NoSuchBeanDefinitionException.

A Választható, tudjuk használni @Autowired’S kívánt attribútum, amelynek értéke: igaz alapértelmezés szerint, hogy a függőség opcionális legyen. Beállíthatjuk a kívánt tulajdonít neki hamis hogy a függőség opcionális legyen az automatikus vezetékezéshez.

Ezért Spring kihagyja a függőség injektálását, ha az adott adattípushoz tartozó bab nem áll rendelkezésre a kontextusban. A függőség továbbra is beállítva lesz nulla:

@ Component public class BookServiceImpl implementálja a BookService {@Autowired (kötelező = hamis) privát AuthorService authorService; }

Néha hasznos lehet a függőségek megjelölése, mivel nem mindig szükséges minden függőség.

Ezt szem előtt tartva emlékeznünk kell arra, hogy fokozott óvatosságra van szükségünk és nulla-ellenőrzések a fejlesztés során annak elkerülése érdekében NullPointerException miatt a nulla függőségek.

3.10. Opcionális függőségek a Guice-ban

Mint Tavaszi, Guice Java 8-okat is használhat Választható írja be, hogy a függőség opcionális legyen.

Tegyük fel, hogy osztályt akarunk létrehozni, és a-val Foo függőség:

nyilvános osztályú FooProcessor {@Inject private Foo foo; }

Most definiáljunk egy kötést a Foo osztály:

bind (Foo.class) .toProvider (new Provider () {public Foo get () {return null;}});

Most próbáljuk meg megszerezni a FooProcessor egység tesztben:

FooProcessor fooProcessor = injector.getInstance (FooProcessor.class); assertNotNull (fooProcessor);

Egységtesztünk sikertelen lesz:

com.google.inject.ProvisionException: null értéket ad vissza a GuiceModule.configure (..) kötéssel, de a FooProcessor 1. paramétere. [...] nem @Nullable

E kivétel kihagyása érdekében megtehetjük a foo függőség opcionális, egyszerű frissítéssel:

nyilvános osztályú FooProcessor {@Inject private Opcionális foo; }

@ Injekció nincs a kívánt attribútum a függőség opcionális megjelölésére. A. Alternatív megközelítése készítsen a Guice-ban választható függőség a @Nullable annotáció.

Guice tűri az injekciót nulla értékek felhasználás esetén @Nullable amint azt a fenti kivételüzenet kifejezte. Alkalmazzuk a @Nullable kommentár:

nyilvános osztályú FooProcessor {@Inject @Nullable private Foo foo; }

4. A függőségi injekció típusainak megvalósítása

Ebben a szakaszban megvizsgáljuk a függőség-injektálás típusait, és számos példán keresztül összehasonlítjuk a Spring és a Guice által biztosított megvalósításokat.

4.1. A kivitelező befecskendezése tavasszal

Konstruktor alapú függőség-injektálás során a szükséges függőségeket egy osztályba adjuk át a példányosítás idején.

Tegyük fel, hogy szeretnénk egy Spring komponenst, és függőségeket szeretnénk hozzáadni a konstruktorán keresztül. Azzal a konstruktort megjegyezhetjük @Autowired:

@ Component public class SpringPersonService {private PersonDao personDao; @Autowired public SpringPersonService (PersonDao personDao) {this.personDao = personDao; }}

A 4. tavasztól kezdve a @Autowired függőség nem szükséges az ilyen típusú injektáláshoz, ha az osztálynak csak egy konstruktora van.

Töltsük le a SpringPersonService bab egy tesztben:

SpringPersonService personService = context.getBean (SpringPersonService.class); assertNotNull (personService);

4.2. Konstruktor injekciója Guice-ban

Átrendezhetjük az előző példát implementálja a konstruktor injekcióját a Guice-ban. Ne feledje, hogy Guice használja @ Injekció ahelyett @Autowired.

nyilvános osztály GuicePersonService {private PersonDao personDao; @ Inject public GuicePersonService (PersonDao personDao) {this.personDao = personDao; }}

Így kaphatunk példányt GuicePersonService osztály a injektor egy teszten:

GuicePersonService personService = injector.getInstance (GuicePersonService.class); assertNotNull (personService);

4.3. Szetter vagy módszer injekció tavasszal

Szetter alapú függőség-injektálás esetén a tároló meghívja az osztály szetter metódusait, miután a konstruktort meghívta az alkatrész példányosítására.

Tegyük fel, hogy azt akarjuk, hogy Spring egy függőséget automatikusan beállítson szetter módszerrel. Azzal a szetter módszert feljegyezhetjük @Autowired:

@ Component public class SpringPersonService {private PersonDao personDao; @Autowired public void setPersonDao (PersonDao personDao) {this.personDao = personDao; }}

Amikor csak szükségünk van a SpringPersonService osztályban, a Spring automatikusan beküldi a personDao mezőt a setPersonDao () módszer.

Kaphatunk egy SpringPersonService babot és érje el personDao mező a tesztben az alábbiak szerint:

SpringPersonService personService = context.getBean (SpringPersonService.class); assertNotNull (personService); assertNotNull (personService.getPersonDao ());

4.4. Szetter vagy módszer injekció Guice-ban

Egyszerűen változtatunk egy kicsit a példánkon, hogy elérjük szetter injekció Guice-ban.

nyilvános osztály GuicePersonService {private PersonDao personDao; @ Inject public void setPersonDao (PersonDao personDao) {this.personDao = personDao; }}

Valahányszor kapunk egy példányt GuicePersonService osztályba az injektorból, meglesz a personDao mező a fenti szetter módszerhez került.

Így hozhatunk létre egy példányt GuicePersonService osztályba, és elérheti azt personDao területegy teszten:

GuicePersonService personService = injector.getInstance (GuicePersonService.class); assertNotNull (personService); assertNotNull (personService.getPersonDao ());

4.5. Helyi injekció tavasszal

Az összes példánkban már láttuk, hogyan kell alkalmazni a terepi injekciót mind Spring, mind Guice esetében. Szóval, ez nem egy új koncepció számunkra. De a teljesség kedvéért csak soroljuk fel újra.

Terepi függőség-injektálás esetén a függőségeket azáltal jelöljük be @Autowired vagy @ Injekció.

4.6. Helyi injekció Guice-ban

Amint azt a fenti szakaszban említettük, már lefedtük a terepi injekció Guice használatához @ Injekció.

5. Következtetés

Ebben az oktatóanyagban megvizsgáltuk a Guice és a Spring keretrendszer közötti számos alapvető különbséget a függőség-injektálás megvalósításának módjaiban. Mint mindig, a Guice és a Spring kódmintáknak is vége a GitHubon.