I forrige artikkel forklarte jeg kort hva vår er og hva bønner og kontekst er. Nå er det på tide å prøve det ut. Jeg skal gjøre det ved hjelp av IntelliJ IDEA Enterprise Edition. Men alle eksemplene mine burde også fungere i den gratis IntelliJ IDEA Community Edition. I skjermbildene, hvis du ser at jeg har et vindu du ikke har, ikke bekymre deg - det er ikke viktig for dette prosjektet :) Først lager du et tomt Maven-prosjekt. Jeg viste hvordan du gjør dette i artikkelen på denne lenken . Les opp til ordene " Det er på tide for oss å konvertere Maven-prosjektet vårt til et nettprosjekt. " - etter det viser artikkelen hvordan du lager et nettprosjekt, men vi trenger det ikke akkurat nå. I src/main/javamappen, opprett en pakke (i mitt tilfelle kalte jeg den "
en.codegym.info.fatfaggy.animals
. Du kan kalle den hva du vil. Bare ikke glem å erstatte pakkenavnet mitt med pakkenavnet ditt på alle de riktige stedene. Opprett nå klassen Main
og legg til en metode
public static void main(String[] args) {
...
}
Etter det åpner du pom.xml-filen og legger til dependencies
delen. Gå nå til Maven-depotet og finn vårkonteksten for den siste stabile versjonen. Legg det vi finner inn i dependencies
seksjonen. Jeg beskrev denne prosessen mer detaljert i denne andre CodeGym-artikkelen (se delen med tittelen " Koble til avhengigheter i Maven "). Da vil Maven selv finne og laste ned de nødvendige avhengighetene. Til slutt skal du få noe slikt: I vinduet til venstre kan du se prosjektstrukturen med pakken og klassen Main
. Det midterste vinduet viser hvordan pom.xml ser ut for meg. Jeg har også lagt til en egenskaperdelen til den. Denne delen forteller Maven hvilken versjon av Java jeg bruker i kildefilene mine og hvilken versjon som skal kompileres. Dette er bare slik at IDEA ikke advarer meg om at jeg bruker en gammel versjon av Java. Dette er valgfritt :) Det høyre vinduet gjør det klart at selv om vi kun koblet til fjær-kontekst-modulen, trakk den automatisk inn fjær-kjerne, fjær-bønner, fjær-aop og fjær-uttrykk-moduler. Vi kunne ha koblet hver modul separat, skrevet ut en avhengighet for hver modul med den eksplisitte versjonen i pom.xml-filen, men foreløpig er vi fornøyde med tingene som de er. Lag nå entities
pakken og lag 3 klasser i den: Cat
, Dog
, Parrot
. La oss gi hvert dyr et navn (private String name
— du kan hardkode noen verdier der). Getterne/setterne er offentlige. Nå går vi videre til Main
klassen og main()
metoden, og vi skriver noe slikt:
public static void main(String[] args) {
// Create an empty Spring context that will look for its own beans based on the annotations in the specified package
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.entities");
Cat cat = context.getBean(Cat.class);
Dog dog = (Dog) context.getBean("dog");
Parrot parrot = context.getBean("parrot-polly", Parrot.class);
System.out.println(cat.getName());
System.out.println(dog.getName());
System.out.println(parrot.getName());
}
Først lager vi et kontekstobjekt som forteller konstruktøren hvilken pakke han skal se i for å finne bønner. Med andre ord, Spring vil gå gjennom denne pakken og prøve å finne klasser merket med spesielle merknader som indikerer at de er bønner. Deretter lager den objektene til disse klassene og setter dem inn i konteksten. Etter det får vi en katt fra denne sammenhengen. Vi ber kontekstobjektet gi oss en bønne (objekt), som indikerer klassen til objektet vi ønsker (forresten, vi kan også spesifisere grensesnitt, ikke bare klasser). Etter det returnerer Spring et objekt av den forespurte klassen, som vi deretter lagrer i en variabel. Deretter ber vi Spring om å skaffe oss en bønne som heter "hund". Når våren skaper enDog
objekt, gir det objektet et standardnavn (med mindre den opprettede bønnen har blitt eksplisitt tildelt et navn), som er klassenavnet, men med en innledende liten bokstav. I dette tilfellet heter klassen vår Dog
, så bønnens navn er være "hund". Hvis vi trengte et BufferedReader
objekt der, ville Spring kalt det "bufferedReader". Og fordi Java ikke kan være 100 % sikker på hvilken klasse vi ønsker, returnerer den et Object
objekt, som vi deretter manuelt caster til ønsket type, dvs.Dog
. Alternativet der klassen er angitt eksplisitt er mer praktisk. Det tredje alternativet er å få en bønne etter klassenavn og bønnenavn. Det er mulig at konteksten kan ha flere bønner av en enkelt klasse. For å indikere den spesielle bønnen vi trenger, angir vi navnet. Fordi vi også eksplisitt angir klassen her, trenger vi ikke lenger utføre en rollebesetning. VIKTIG!Hvis Spring finner flere bønner som samsvarer med kravene våre, kan den ikke bestemme hvilken bønne vi skal gi oss, så det vil gi et unntak. Derfor, for å unngå denne situasjonen, bør du prøve å være så spesifikk som mulig når du forteller Spring hvilken bønne du trenger. Hvis Spring søker sin kontekst og ikke finner en enkelt bønne som samsvarer med kravene våre, vil den også gi et unntak. Til slutt viser vi ganske enkelt navnene på dyrene våre for å bekrefte at vi virkelig har de gjenstandene vi trenger. Men hvis vi kjører programmet nå, vil vi se at våren er ulykkelig - den kan ikke finne dyrene vi trenger i sin kontekst. Dette er fordi det ikke har skapt disse bønnene. Som jeg sa tidligere, når Spring skanner klasser, ser den etter sine egne Spring-kommentarer. Og hvis våren ikke finner disse kommentarene, så gjør den ikke Jeg tror ikke at disse klassene tilsvarer bønner som den trenger å lage. Å fikse dette krever ganske enkelt å legge til@Component
merknad foran hver av våre dyreklasser.
@Component
public class Cat {
private String name = "Oscar";
...
}
Men det er mer. Hvis vi eksplisitt trenger å fortelle Spring at bønnen for denne klassen skal ha et spesifikt navn, angir vi navnet i parentes etter merknaden. For å for eksempel be Spring om å gi navnet " parrot-polly
" til papegøyebønnen, som er navnet vi skal bruke for å få denne papegøyen i metoden main
, bør vi gjøre noe slikt:
@Component("parrot-polly")
public class Parrot {
private String name = "Polly";
...
}
Dette er hele poenget med automatisk konfigurasjon . Du skriver klassene dine, merker dem med de nødvendige merknadene, og forteller Spring pakken som inneholder klassene dine. Dette er pakken som rammeverket vil gå gjennom for å finne merknader og lage objekter av disse klassene. Spring ser forresten ikke bare etter @Component
merknader, men også alle andre merknader som arver denne. For eksempel, @Controller
, , , og mer, som vi vil introdusere i fremtidige artikler @RestController
. Nå skal vi prøve å gjøre det samme ved å bruke Java-basert konfigurasjon . For å komme i gang, fjern@Service
@Repository
@Component
merknader fra våre klasser. For å gjøre ting mer utfordrende, se for deg at vi ikke skrev disse klassene, så vi kan ikke enkelt endre dem, noe som betyr at vi ikke kan legge til merknader. Det er som om disse klassene er pakket i et eller annet bibliotek. I dette tilfellet er det ingen måte for oss å redigere disse klassene slik at de gjenkjennes av Spring. Men vi trenger objekter av disse klassene! Her trenger vi Java-basert konfigurasjon for å lage objektene. For å komme i gang, lag en pakke med et navn som configs
. I denne pakken oppretter du en vanlig Java-klasse, noe sånt som MyConfig
, og merker den med @Configuration
merknaden.
@Configuration
public class MyConfig {
}
Nå må vi finpusse main()
metoden, endre hvordan vi skaper konteksten. Vi kan enten eksplisitt indikere hvilken klasse som har vår konfigurasjon:
ApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class);
Hvis vi har flere forskjellige klasser som lager bønner og vi ønsker å koble sammen flere av dem samtidig, indikerer vi dem alle der, atskilt med kommaer:
ApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class, MyAnotherConfig.class);
Og hvis vi har for mange av dem og vi ønsker å koble dem alle samtidig, indikerer vi ganske enkelt navnet på pakken de er inneholdt i:
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.configs");
I dette tilfellet vil Spring gå gjennom pakken og finne alle klassene merket med merknaden @Configuration
. Vel, og hvis vi har et veldig stort program der konfigurasjonene er delt inn i forskjellige pakker, så indikerer vi ganske enkelt en kommadelt liste over navn på pakkene som inneholder konfigurasjonene:
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.database.configs",
"en.codegym.info.fatfaggy.animals.root.configs",
"en.codegym.info.fatfaggy.animals.web.configs");
Eller navnet på en pakke som er felles for dem alle:
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals");
Du kan gjøre det som du vil, men det ser ut til at det aller første alternativet, som ganske enkelt indikerer en klasse med konfigurasjonene, vil passe programmet vårt best. Når du oppretter en kontekst, ser Spring etter klasser merket med @Configuration
merknaden og vil lage sine egne objekter av disse klassene. Den prøver å kalle metoder merket med @Bean
merknaden, noe som betyr at disse metodene returnerer bønner (objekter) som Spring vil legge til konteksten. Og nå skal vi lage bønner for en katt, hund og papegøye i klassen vår med Java-basert konfigurasjon. Dette er ganske enkelt å gjøre:
@Bean
public Cat getCat() {
return new Cat();
}
Her lager vi katten vår manuelt og leverer den til Spring, som deretter holder objektet vårt i sin kontekst. Siden vi ikke eksplisitt ga et navn til bønnen vår, vil Spring gi den samme navn som navnet på metoden. I vårt tilfelle vil kattebønnen bli kalt " getCat
". Men fordi vi bruker klassen, ikke navnet, for å få kattebønnen i metoden main
, er ikke bønnens navn viktig for oss. På samme måte lager du en hundebønne, og husk at Spring vil gi metodenavnet til bønnen. For å eksplisitt navngi papegøyebønnen vår, angir vi ganske enkelt navnet i parentes etter merknaden @Bean
:
@Bean("parrot-polly")
public Object weNeedMoreParrots() {
return new Parrot();
}
Som du kan se, indikerte jeg her en Object
returtype og ga metoden et vilkårlig navn. Dette påvirker ikke bønnens navn, fordi vi har spesifisert navnet her. Likevel er det bedre å angi en mer eller mindre meningsfull returverdi og metodenavn. Gjør dette om ikke annet enn å gjøre deg selv en tjeneste når du gjenåpner prosjektet om et år. :) Vurder nå situasjonen der vi trenger en bønne for å lage en annen bønne . Anta for eksempel at vi vil at navnet på katten i kattebønnen skal være papegøyens navn pluss strengen "-killer". Ikke noe problem!
@Bean
public Cat getCat(Parrot parrot) {
Cat cat = new Cat();
cat.setName(parrot.getName() + "-killer");
return cat;
}
Her vil våren se at for å lage denne bønnen, må rammeverket passere i den tidligere opprettede papegøyebønnen. Følgelig vil den ordne den nødvendige kjeden av metodekall: først kalles den papegøyeskapende metoden, og deretter overfører rammeverket den nye papegøyen til katteopprettingsmetoden. Her er hvor avhengighetsinjeksjon kommer inn i bildet: Våren i seg selv sender den nødvendige papegøyebønnen til metoden vår. Hvis IDEA blir opprørt over parrot
variabelen, ikke glem å endre returtypen til den papegøyeskapende metoden fra Object
til Parrot
. I tillegg lar Java-basert konfigurasjon deg kjøre absolutt hvilken som helst Java-kodei dine bønneskapende metoder. Du kan virkelig gjøre hva som helst: lage andre hjelpeobjekter, kalle alle andre metoder, også de som ikke er merket med vårmerknader, lage looper, boolske forhold – uansett hva du tenker på! Dette er ikke alt mulig med automatisk konfigurasjon, og enda mindre med XML-konfigurasjon. La oss nå vurdere et problem som er litt morsommere. Polymorfisme og grensesnitt :) Vi lager et WeekDay
grensesnitt og lager 7 klasser som implementerer dette grensesnittet: Monday
, Tuesday
, Wednesday
, Thursday
, Friday
, Saturday
, Sunday
. Vi vil gi grensesnittet en String getWeekDayName()
metode som vil returnere navnet på ukedagen for den tilsvarende klassen. Med andre ord, Monday
klassen kommer tilbake "Monday
", osv. Når du starter programmet, anta at vår oppgave er å sette en bønne som tilsvarer gjeldende ukedag inn i konteksten. Ikke bønner for alle klassene som implementerer grensesnittet - bare den ene bønnen vi trenger. Du WeekDay
kan gjør det omtrent slik:
@Bean
public WeekDay getDay() {
DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
switch (dayOfWeek) {
case MONDAY: return new Monday();
case TUESDAY: return new Tuesday();
case WEDNESDAY: return new Wednesday();
case THURSDAY: return new Thursday();
case FRIDAY: return new Friday();
case SATURDAY: return new Saturday();
default: return new Sunday();
}
}
Her er returtypen vårt grensesnitt. Metoden returnerer et bona fide-objekt fra en av klassene som implementerer grensesnittet, avhengig av gjeldende ukedag. Nå kan vi gjøre følgende i main()
metoden:
WeekDay weekDay = context.getBean(WeekDay.class);
System.out.println("Today is " + weekDay.getWeekDayName() + "!");
For meg forteller programmet meg at det er søndag :) Jeg er sikker på at hvis jeg kjører programmet i morgen, vil konteksten inneholde et helt annet objekt. Merk at vi får bønnen ganske enkelt ved å bruke grensesnittet: context.getBean(WeekDay.class)
. Spring vil søke sin kontekst etter bønnen som implementerer grensesnittet, og vil returnere den. Så viser det seg at WeekDay
variabelen vår ender opp med et søndagsobjekt, og det kjente begrepet polymorfisme gjelder når vi jobber med denne variabelen. :) Nå noen få ord om den kombinerte tilnærmingen , der noen bønner opprettes automatisk av Spring, noen ved å skanne pakker for klasser med merknaden @Component
, og andre av Java-basert konfigurasjon. Når vi vurderer dette, går vi tilbake til den opprinnelige versjonen, der Cat
, Dog
, ogParrot
klassene ble merket med @Component
merknaden. Tenk deg at vi ønsker å lage bønner for dyrene våre ved å la Spring automatisk skanne pakken entities
, men vi ønsker også å lage en bønne med ukedagen, slik vi nettopp gjorde. Alt du trenger å gjøre er å legge til @ComponentScan
merknaden på klassenivå MyConfig
, som vi angir når du oppretter konteksten i main()
, og angi i parentes pakken som skal skannes og lage bønner av de nødvendige klassene automatisk:
@Configuration
@ComponentScan("en.codegym.info.fatfaggy.animals.entities")
public class MyConfig {
@Bean
public WeekDay getDay() {
DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
switch (dayOfWeek) {
case MONDAY: return new Monday();
case TUESDAY: return new Tuesday();
case WEDNESDAY: return new Wednesday();
case THURSDAY: return new Thursday();
case FRIDAY: return new Friday();
case SATURDAY: return new Saturday();
default: return new Sunday();
}
}
}
Når vi lager konteksten, ser Spring at den må bearbeide klassen MyConfig
. Den går inn i klassen og ser at den må skanne " en.codegym.info.fatfaggy.animals.entities
"-pakken og lage bønner av disse klassene, hvoretter den kjører MyConfig
klassens getDay()
metode og legger til en WeekDay
bean til konteksten. I main()
metoden har vi nå tilgang til alle bønnene vi trenger: både dyregjenstander og en bønne med ukedagen. Hvis du noen gang trenger å få Spring til å plukke opp noen XML-konfigurasjonsfiler, kan du gjøre ditt eget nettsøk for å finne en forklaring :) Sammendrag:
- Prøv å bruke automatisk konfigurasjon
- Under automatisk konfigurasjon, angi navnet på pakken som inneholder klassene hvis bønner må opprettes
- Disse klassene er merket med
@Component
merknaden - Våren går gjennom alle disse klassene, lager objekter og setter dem inn i konteksten;
- Hvis automatisk konfigurasjon av en eller annen grunn ikke passer oss, bruker vi Java-basert konfigurasjon
- I dette tilfellet lager vi en vanlig Java-klasse hvis metoder returnerer objektene vi trenger. Vi merker denne klassen med
@Configuration
merknaden hvis vi skal skanne hele pakken i stedet for å angi en spesifikk klasse med konfigurasjonen når vi oppretter konteksten - Metodene i denne klassen som returnerer bønner er merket med
@Bean
merknaden
@ComponentScan
.
GO TO FULL VERSION