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
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.