Guice vs Spring - Avhengighetsinjeksjon

1. Introduksjon

Google Guice og Vår er to robuste rammer som brukes til avhengighetsinjeksjon. Begge rammene dekker alle forestillingene om avhengighetsinjeksjon, men hver har sin egen måte å implementere dem på.

I denne veiledningen vil vi diskutere hvordan Guice og Spring rammeverkene er forskjellige i konfigurasjon og implementering.

2. Maven-avhengigheter

La oss starte med å legge til Guice og Spring Maven avhengighet i vår pom.xml fil:

 org.springframework spring-context 5.1.4.RELEASE com.google.inject guice 4.2.2 

Vi har alltid tilgang til det siste vår-kontekst eller guice avhengigheter fra Maven Central.

3. Avhengighetsinjeksjonskonfigurasjon

Avhengighetsinjeksjon er en programmeringsteknikk som vi bruker for å gjøre klassene våre uavhengige av deres avhengighet.

I denne delen vil vi referere til flere kjernefunksjoner som skiller seg mellom Spring og Guice når det gjelder å konfigurere avhengighetsinjeksjon.

3.1. Vårledninger

Spring erklærer konfigurasjoner for avhengighetsinjeksjon i en spesiell konfigurasjonsklasse. Denne klassen må kommenteres av @Konfigurasjon kommentar. Spring container bruker denne klassen som en kilde til bønnedefinisjoner.

Klasser som administreres av Spring kallesVårbønner.

Våren bruker @Autowired kommentar for å koble avhengighetene automatisk. @Autowired er en del av Spring's innebygde kjerneannotasjoner. Vi kan bruke @Autowired på medlemsvariabler, settermetoder og konstruktører.

Våren støtter også @Injiser. @Injiser er en del av Java CDI (Contexts and Dependency Injection) som definerer en standard for avhengighetsinjeksjon.

La oss si at vi automatisk vil koble en avhengighet til en medlemsvariabel. Vi kan ganske enkelt kommentere det med @Autowired:

@Component public class UserService {@Autowired private AccountService accountService; }
@Component public class AccountServiceImpl implementerer AccountService {}

For det andre, la oss lage en konfigurasjonsklasse som skal brukes som kilde til bønner mens vi laster inn applikasjonskonteksten:

@Configuration @ComponentScan ("com.baeldung.di.spring") offentlig klasse SpringMainConfig {}

Merk at vi også har kommentert UserService og AccountServiceImpl med @Komponent å registrere dem som bønner. Det er @ComponentScan kommentar som vil fortelle våren hvor du skal søke for merkede komponenter.

Selv om vi har kommentert AccountServiceImpl,Våren kan kartlegge det til AccountService siden den implementerer AccountService.

Deretter må vi definere en applikasjonskontekst for å få tilgang til bønnene. La oss bare merke oss at vi vil referere til denne sammenhengen i alle våre vårenhetstester:

ApplicationContext context = new AnnotationConfigApplicationContext (SpringMainConfig.class);

Nå ved kjøretid kan vi hente ENccountService eksempel fra vår UserService bønne:

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

3.2. Guice Binding

Guice administrerer sine avhengigheter i en spesiell klasse som kalles en modul. En Guice-modul må utvide Abstrakt modul klasse og overstyre dens konfigurer () metode.

Guice bruker binding som tilsvarer ledninger på våren. For å si det enkelt, bindinger lar oss definere hvordan avhengigheter skal injiseres i en klasse. Veiledningsbindinger er deklarert i modulene våre konfigurer () metode.

I stedet for @Autowired, Guice bruker @Injiser kommentar for å injisere avhengighetene.

La oss lage et tilsvarende Guice-eksempel:

offentlig klasse GuiceUserService {@Inject private AccountService accountService; }

For det andre lager vi modulklassen som er en kilde til våre bindende definisjoner:

offentlig klasse GuiceModule utvider AbstractModule {@Override-beskyttet tom konfigurere () {bind (AccountService.class) .to (AccountServiceImpl.class); }}

Normalt forventer vi at Guice vil instansiere hvert avhengighetsobjekt fra standardkonstruktørene hvis det ikke er noen binding definert eksplisitt i konfigurer () metode. Men siden grensesnitt ikke kan instantieres direkte, må vi definere bindinger for å fortelle Guice hvilket grensesnitt som vil bli parret med hvilken implementering.

Deretter må vi definere en Injektor ved hjelp av GuiceModule for å få forekomster av klassene våre. La oss bare merke oss at alle Guice-testene våre vil bruke dette Injektor:

Injektorinjektor = Guice.createInjector (ny GuiceModule ());

Til slutt, når vi kjører, henter vi a GuiceUserService forekomst med en ikke-null accountService avhengighet:

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

3.3. Vårens @Bean-kommentar

Våren gir også en merknadsnivåkommentar @Bønne å registrere bønner som et alternativ til klassenivåkommentarer som @Komponent. Returverdien til en @Bønne merket metode er registrert som en bønne i containeren.

La oss si at vi har en forekomst av BookServiceImpl som vi ønsker å gjøre tilgjengelig for injeksjon. Vi kan bruke @Bønne for å registrere vår forekomst:

@Bean public BookService bookServiceGenerator () {returner ny BookServiceImpl (); }

Og nå kan vi få en BookService bønne:

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

3.4. Guice's @Provides Annotation

Som tilsvarer vårens @Bønne kommentar, Guice har en innebygd kommentar @Leverer å gjøre den samme jobben. Som @Bønne, @Leverer brukes bare på metodene.

La oss nå implementere det forrige eksemplet på vårbønne med Guice. Alt vi trenger å gjøre er å legge til følgende kode i modulklassen vår:

@Leverer offentlig BookService bookServiceGenerator () {returner ny BookServiceImpl (); }

Og nå kan vi hente en forekomst av BookService:

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

3.5. Classpath komponentskanning om våren

Våren gir en @ComponentScan kommentar oppdager og instantierer merkede komponenter automatisk ved å skanne forhåndsdefinerte pakker.

De @ComponentScan kommentar forteller Spring hvilke pakker som skal skannes for merkede komponenter. Den brukes med @Konfigurasjon kommentar.

3.6. Classpath komponentskanning i Guice

I motsetning til våren, Guice har ikke en slik funksjon for skanning av komponenter. Men det er ikke vanskelig å simulere det. Det er noen plugins som Guvernør som kan bringe denne funksjonen inn i Guice.

3.7. Objektgjenkjenning om våren

Våren kjenner gjenstander etter navn. Våren holder gjenstandene i en struktur som er omtrent som en Kart. Dette betyr at vi ikke kan ha to objekter med samme navn.

Bønnekollisjon på grunn av å ha flere bønner med samme navn er et vanlig problem Vårutviklere traff. La oss for eksempel vurdere følgende bønnedeklarasjoner:

@Configuration @Import ({SpringBeansConfig.class}) @ComponentScan ("com.baeldung.di.spring") public class SpringMainConfig {@Bean public BookService bookServiceGenerator () {return new BookServiceImpl (); }}
@Configuration public class SpringBeansConfig {@Bean public AudioBookService bookServiceGenerator () {returner ny AudioBookServiceImpl (); }}

Som vi husker hadde vi allerede en bønnedefinisjon for BookService i SpringMainConfig klasse.

For å skape en bønnekollisjon her, må vi erklære bønnemetodene med samme navn. Men vi har ikke lov til å ha to forskjellige metoder med samme navn i en klasse. Av den grunn erklærte vi at AudioBookService bønne i en annen konfigurasjonsklasse.

La oss nå henvise til disse bønnene i en enhetstest:

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

Enhetstesten mislykkes med:

org.springframework.beans.factory.NoSuchBeanDefinitionException: Ingen kvalifiserende bønner av typen 'AudioBookService' tilgjengelig

Først registrerte Spring AudioBookService bønne med “BookServiceGenerator” navnet i bønnekartet. Deretter måtte den overstyre den etter bønnedefinisjonen for BookService på grunn av “Ingen dupliserte navn tillatt” arten av HashMap data struktur.

Til slutt, vi kan løse dette problemet ved å gjøre navnene på bønnemetoden unike eller sette Navn attributt til et unikt navn for hver @Bønne.

3.8. Objektgjenkjenning i Guice

I motsetning til våren, Guice har i utgangspunktet en Kart struktur. Dette betyr at vi ikke kan ha flere bindinger til samme type uten å bruke ekstra metadata.

Guice gir bindende merknader for å muliggjøre definering av flere bindinger for samme type. La oss se hva som skjer hvis vi har to forskjellige bindinger for samme type i Guice.

offentlig klasse person {}

La oss nå erklære to forskjellige bindende for Person klasse:

bind (Person.class) .toConstructor (Person.class.getConstructor ()); bind (Person.class) .toProvider (new Provider () {public Person get () {Person p = new Person (); return p;}});

Og her er hvordan vi kan få en forekomst av Person klasse:

Personperson = injektor.getInstance (Person.klasse); assertNotNull (person);

Dette vil mislykkes med:

com.google.inject.CreationException: En binding til Person var allerede konfigurert på GuiceModule.configure ()

Vi kan løse dette problemet ved å bare kaste en av bindingene for Person klasse.

3.9. Valgfrie avhengigheter om våren

Valgfrie avhengigheter er avhengigheter som ikke er påkrevd når du kobler kabelen automatisk eller injiserer bønner.

For et felt som er merket med @Autowired, hvis en bønne med samsvarende datatype ikke blir funnet i sammenhengen, vil Spring kaste NoSuchBeanDefinitionException.

Derimot, noen ganger kan det være lurt å hoppe over autoledning for noen avhengigheter og la dem være nulluten å kaste et unntak:

La oss nå se på følgende eksempel:

@Component public class BookServiceImpl implementerer BookService {@Autowired private AuthorService authorService; }
offentlig klasse AuthorServiceImpl implementerer AuthorService {}

Som vi kan se fra koden ovenfor, AuthorServiceImpl klasse har ikke blitt kommentert som en komponent. Og vi antar at det ikke er en bønnedeklarasjonsmetode for det i konfigurasjonsfilene våre.

La oss nå kjøre følgende test for å se hva som skjer:

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

Ikke overraskende vil det mislykkes med:

org.springframework.beans.factory.NoSuchBeanDefinitionException: Ingen kvalifiserende bønner av typen 'AuthorService' tilgjengelig

Vi kan lage authorService avhengighet valgfritt ved hjelp av Java 8-er Valgfri skriv for å unngå dette unntaket.

offentlig klasse BookServiceImpl implementerer BookService {@Autowired private Valgfri authorService; }

Nå, vår authorService avhengighet er mer som en beholder som kan eller ikke kan inneholde en bønne av AuthorService type. Selv om det ikke er en bønne til AuthorService i vår søknadssammenheng, vår authorService feltet vil fortsatt være ikke-null tom beholder. Derfor vil ikke Spring ha noen grunn til å kaste NoSuchBeanDefinitionException.

Som et alternativ til Valgfri, Vi kan bruke @Autowired‘S kreves attributt, som er satt til ekte som standard for å gjøre en avhengighet valgfri. Vi kan stille inn kreves tilskrive falsk for å gjøre en avhengighet valgfri for autoledning.

Derfor vil Spring hoppe over å injisere avhengigheten hvis en bønne for datatypen ikke er tilgjengelig i sammenhengen. Avhengigheten vil forbli satt til null:

@Component public class BookServiceImpl implementerer BookService {@Autowired (obligatorisk = falsk) privat AuthorService authorService; }

Noen ganger kan det være nyttig å merke avhengigheter valgfritt, ettersom ikke alle avhengighetene alltid kreves.

Med dette i bakhodet, bør vi huske at vi må være ekstra forsiktige og null-sjekker under utvikling for å unngå noe NullPointerException på grunn av null avhengigheter.

3.10. Valgfrie avhengigheter i Guice

Akkurat som Vår, Guice kan også bruke Java 8-er Valgfri skriv for å gjøre en avhengighet valgfri.

La oss si at vi ønsker å lage en klasse og med en Foo avhengighet:

offentlig klasse FooProcessor {@Inject private Foo foo; }

La oss nå definere en binding for Foo klasse:

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

La oss nå prøve å få en forekomst av FooProcessor i en enhetstest:

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

Enhetstesten vår mislykkes med:

com.google.inject.ProvisionException: null returnert ved binding på GuiceModule.configure (..) men den første parameteren til FooProcessor. [...] er ikke @Nullable

For å hoppe over dette unntaket kan vi lage foo avhengighet valgfritt med en enkel oppdatering:

offentlig klasse FooProcessor {@Inject private Valgfri foo; }

@Injiser har ikke en kreves attributt for å markere avhengigheten valgfri. En alternativ tilnærming til gjøre en avhengighet valgfri i Guice er å bruke @Nullable kommentar.

Guice tåler injeksjon null verdier ved bruk @Nullable som uttrykt i unntaksmeldingen ovenfor. La oss bruke @Nullable kommentar:

offentlig klasse FooProcessor {@Inject @Nullable private Foo foo; }

4. Implementeringer av avhengighetsinjeksjonstyper

I denne delen tar vi en titt på avhengighetsinjeksjonstyper og sammenligner implementeringene som er gitt av Spring og Guice ved å gå gjennom flere eksempler.

4.1. Constructor Injection om våren

I konstruktørbasert avhengighetsinjeksjon sender vi de nødvendige avhengighetene inn i en klasse på tidspunktet for instantiering.

La oss si at vi vil ha en vårkomponent, og vi vil legge til avhengigheter gjennom konstruktøren. Vi kan kommentere konstruktøren med @Autowired:

@Komponent offentlig klasse SpringPersonService {privat PersonDao personDao; @Autowired public SpringPersonService (PersonDao personDao) {this.personDao = personDao; }}

Fra og med vår 4, den @Autowired avhengighet er ikke nødvendig for denne typen injeksjon hvis klassen bare har en konstruktør.

La oss hente en SpringPersonService bønne i en test:

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

4.2. Constructor Injection in Guice

Vi kan omorganisere det forrige eksemplet til implementere konstruktørinjeksjon i Guice. Merk at Guice bruker @Injiser i stedet for @Autowired.

offentlig klasse GuicePersonService {privat PersonDao personDao; @Inject public GuicePersonService (PersonDao personDao) {this.personDao = personDao; }}

Slik kan vi få en forekomst av GuicePersonService klasse fra injektor i en test:

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

4.3. Setter eller metodeinjeksjon om våren

Ved setterbasert avhengighetsinjeksjon vil beholderen ringe settermetoder i klassen, etter å ha påkalt konstruktøren for å instantiere komponenten.

La oss si at vi vil at våren skal autowire en avhengighet ved hjelp av en settermetode. Vi kan kommentere den settermetoden med @Autowired:

@Komponent offentlig klasse SpringPersonService {privat PersonDao personDao; @Autowired public void setPersonDao (PersonDao personDao) {this.personDao = personDao; }}

Når vi trenger en forekomst av SpringPersonService klasse, våren vil autoledre personDao feltet ved å påkalle setPersonDao () metode.

Vi kan få en SpringPersonService bønne og få tilgang til den personDao felt i en test som nedenfor:

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

4.4. Setter eller metodeinjeksjon i Guice

Vi vil bare endre eksemplet vårt litt for å oppnå setterinjeksjon i Guice.

offentlig klasse GuicePersonService {privat PersonDao personDao; @Inject public void setPersonDao (PersonDao personDao) {this.personDao = personDao; }}

Hver gang vi får en forekomst av GuicePersonService klasse fra injektoren, får vi personDao felt overført til settermetoden ovenfor.

Slik kan vi lage en forekomst av GuicePersonService klasse og få tilgang til dens personDao felti en test:

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

4.5. Feltinjeksjon om våren

Vi har allerede sett hvordan vi skal bruke feltinjeksjon både for vår og Guice i alle eksemplene våre. Så det er ikke et nytt konsept for oss. Men la oss bare liste det igjen for fullstendighet.

I tilfelle feltbasert avhengighetsinjeksjon, injiserer vi avhengighetene ved å merke dem med @Autowired eller @Injiser.

4.6. Feltinjeksjon i Guice

Som vi nevnte i avsnittet ovenfor, dekket vi allerede feltinjeksjon for Guice ved bruk @Injiser.

5. Konklusjon

I denne opplæringen undersøkte vi de mange kjerneforskjellene mellom Guice og Spring-rammeverket i deres måter å implementere avhengighetsinjeksjon på. Som alltid er Guice og Spring-kodeeksempler over på GitHub.


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