In het vorige artikel heb ik kort uitgelegd wat lente is en wat boontjes en context zijn. Nu is het tijd om het uit te proberen. Ik ga het doen met IntelliJ IDEA Enterprise Edition. Maar al mijn voorbeelden zouden ook moeten werken in de gratis IntelliJ IDEA Community Edition. Als je in de screenshots ziet dat ik een venster heb dat jij niet hebt, maak je geen zorgen - het is niet belangrijk voor dit project :) Maak eerst een leeg Maven-project. Ik liet zien hoe dit te doen in het artikel op deze link . Lees tot de woorden " Het is tijd voor ons om ons Maven-project om te zetten in een webproject. " - daarna laat het artikel zien hoe je een webproject kunt maken, maar dat hebben we nu niet nodig. In de src/main/javamap, maak een pakket aan (in mijn geval noemde ik het "
en.codegym.info.fatfaggy.animals
. Je kunt het noemen wat je wilt. Vergeet alleen niet om mijn pakketnaam op de juiste plaatsen te vervangen door jouw pakketnaam. Maak nu de Main
klas aan en voeg een methode
public static void main(String[] args) {
...
}
Open daarna het bestand pom.xml en voeg de dependencies
sectie toe. Ga nu naar de Maven-repository en zoek de Spring-context voor de nieuwste stabiele versie. Zet wat we vinden in de dependencies
sectie. Ik heb dit proces in meer detail beschreven in dit andere CodeGym-artikel (zie de sectie getiteld " Afhankelijkheden verbinden in Maven "). Vervolgens zoekt en downloadt Maven zelf de benodigde afhankelijkheden. Uiteindelijk zou je zoiets moeten krijgen: In het venster aan de linkerkant zie je de projectstructuur met het pakket en de Main
klas. Het middelste venster laat zien hoe pom.xml er voor mij uitziet. Ik heb ook een eigenschappen toegevoegdsectie eraan. Deze sectie vertelt Maven welke versie van Java ik gebruik in mijn bronbestanden en welke versie ik moet compileren. Dit is zodat IDEA me niet waarschuwt dat ik een oude versie van Java gebruik. Dit is optioneel :) Het rechtervenster maakt duidelijk dat hoewel we alleen de spring-context-module hebben aangesloten, deze automatisch de spring-core-, spring-beans-, spring-aop- en spring-expressie-modules binnenhaalt. We hadden elke module afzonderlijk kunnen verbinden, door een afhankelijkheid voor elke module uit te schrijven met de expliciete versie in het pom.xml-bestand, maar voor nu zijn we tevreden met de dingen zoals ze zijn. Maak nu het entities
pakket en maak er 3 klassen in: Cat
, Dog
, Parrot
. Laten we elk dier een naam geven (private String name
- u kunt daar enkele waarden hardcoderen). De getters/setters zijn openbaar. Nu gaan we verder met de Main
klasse en de main()
methode, en we schrijven zoiets als dit:
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());
}
Eerst maken we een contextobject, dat de constructor vertelt in welk pakket hij moet kijken om bonen te vinden. Met andere woorden, Spring zal dit pakket doornemen en klassen proberen te vinden die zijn gemarkeerd met speciale annotaties die aangeven dat het bonen zijn. Vervolgens worden de objecten van deze klassen gemaakt en in de context geplaatst. Daarna krijgen we een kat uit deze context. We roepen het contextobject aan om ons een bean (object) te geven, die de klasse aangeeft van het object dat we willen (trouwens, we kunnen ook interfaces specificeren, niet alleen klassen). Daarna retourneert Spring een object van de gevraagde klasse, dat we vervolgens opslaan in een variabele. Vervolgens vragen we Spring om ons een boon genaamd "hond" te geven. Wanneer de lente eenDog
object, geeft het het object een standaardnaam (tenzij de gemaakte bean expliciet een naam heeft gekregen), wat de klassenaam is maar met een kleine letter aan het begin. In dit geval heet onze klasse Dog
, dus de naam van de boon is be "dog". Als we BufferedReader
daar een object nodig hadden, zou Spring het "bufferedReader" noemen. En omdat Java niet 100% zeker kan zijn welke klasse we willen, retourneert het een Object
object, dat we vervolgens handmatig casten naar het gewenste type, dwzDog
. Handiger is de optie waarbij de klasse expliciet wordt aangegeven. De derde optie is om een bean op klassenaam en op beannaam te krijgen. Het is mogelijk dat de context meerdere bonen van een enkele klasse heeft. Om aan te geven welke boon we nodig hebben, geven we de naam aan. Omdat we hier ook expliciet de klasse aangeven, hoeven we geen cast meer uit te voeren. BELANGRIJK!Als Spring verschillende bonen vindt die aan onze eisen voldoen, kan het niet bepalen welke boon het ons moet geven, dus zal het een uitzondering genereren. Om deze situatie te voorkomen, moet je daarom proberen zo specifiek mogelijk te zijn om Spring te vertellen welke boon je nodig hebt. Als Spring de context doorzoekt en er niet in slaagt een enkele boon te vinden die aan onze vereisten voldoet, dan zal het ook een uitzondering genereren. Ten slotte tonen we gewoon de namen van onze dieren om te verifiëren dat we echt de objecten hebben die we nodig hebben. Maar als we het programma nu uitvoeren, zullen we zien dat Spring ongelukkig is - het kan de dieren die we nodig hebben niet vinden in zijn context. Dit komt omdat het deze bonen niet heeft gemaakt. Zoals ik al eerder zei, wanneer Spring klassen scant, zoekt het naar zijn eigen Spring-annotaties. En als Spring deze annotaties niet vindt, dan doet het ' Ik denk niet dat deze klassen overeenkomen met de bonen die het moet maken. Om dit op te lossen, hoeft u alleen maar het@Component
annotatie voor elk van onze dierenklassen.
@Component
public class Cat {
private String name = "Oscar";
...
}
Maar er is meer. Als we Spring expliciet moeten vertellen dat de bean voor deze klasse een specifieke naam moet hebben, geven we de naam tussen haakjes aan na de annotatie. Om bijvoorbeeld Spring te vertellen om parrot-polly
de papegaaiboon de naam " " te geven, wat de naam is die we zullen gebruiken om deze papegaai in de main
methode te krijgen, zouden we zoiets als dit moeten doen:
@Component("parrot-polly")
public class Parrot {
private String name = "Polly";
...
}
Dit is het hele punt van automatische configuratie . U schrijft uw lessen, markeert ze met de nodige annotaties en vertelt Spring het pakket dat uw lessen heeft. Dit is het pakket dat het framework zal doorlopen om annotaties te vinden en objecten van deze klassen te maken. Overigens zoekt Spring niet alleen naar @Component
annotaties, maar ook naar alle andere annotaties die deze erven. Bijvoorbeeld, @Controller
, @RestController
, @Service
, @Repository
en meer, die we in toekomstige artikelen zullen introduceren. Nu zullen we proberen hetzelfde te doen met behulp van op Java gebaseerde configuratie . Verwijder om te beginnen het@Component
aantekeningen uit onze lessen. Om het nog uitdagender te maken, stel je voor dat we deze klassen niet hebben geschreven, dus we kunnen ze niet gemakkelijk wijzigen, wat betekent dat we geen annotaties kunnen toevoegen. Het is alsof deze klassen in een of andere bibliotheek zijn verpakt. In dit geval kunnen we deze klassen niet bewerken zodat ze door Spring worden herkend. Maar we hebben objecten van deze klassen nodig! Hier hebben we op Java gebaseerde configuratie nodig om de objecten te maken. Maak om te beginnen een pakket aan met een naam als configs
. Maak in dit pakket een gewone Java-klasse, zoiets als MyConfig
, en markeer deze met de @Configuration
annotatie.
@Configuration
public class MyConfig {
}
Nu moeten we de methode aanpassen main()
en de manier wijzigen waarop we de context creëren. We kunnen expliciet aangeven welke klasse onze configuratie heeft:
ApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class);
Als we verschillende klassen hebben die bonen maken en we willen er meerdere tegelijk verbinden, dan geven we ze daar gewoon allemaal aan, gescheiden door komma's:
ApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class, MyAnotherConfig.class);
En als we er te veel hebben en we ze allemaal tegelijk willen aansluiten, dan geven we gewoon de naam van het pakket aan waarin ze zitten:
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals.configs");
In dit geval zal Spring het pakket doornemen en alle klassen zoeken die zijn gemarkeerd met de @Configuration
annotatie. Welnu, en als we een heel groot programma hebben waarin de configuraties zijn onderverdeeld in verschillende pakketten, dan geven we gewoon een door komma's gescheiden lijst met namen van de pakketten die de configuraties bevatten:
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");
Of de naam van een pakket dat ze allemaal gemeen hebben:
ApplicationContext context =
new AnnotationConfigApplicationContext("en.codegym.info.fatfaggy.animals");
Je kunt het doen zoals je wilt, maar het lijkt mij dat de allereerste optie, die simpelweg een klasse aangeeft met de configuraties, het beste bij ons programma past. Bij het maken van een context zoekt Spring naar klassen die zijn gemarkeerd met de @Configuration
annotatie en maakt zijn eigen objecten van deze klassen. Het probeert methoden aan te roepen die zijn gemarkeerd met de @Bean
annotatie, wat betekent dat deze methoden bonen (objecten) retourneren die Spring aan de context zal toevoegen. En nu gaan we bonen maken voor een kat, hond en papegaai in onze klas met op Java gebaseerde configuratie. Dit is vrij eenvoudig te doen:
@Bean
public Cat getCat() {
return new Cat();
}
Hier maken we handmatig onze kat en geven deze aan Spring, die ons object vervolgens in zijn context houdt. Omdat we onze bean niet expliciet een naam hebben gegeven, zal Spring deze dezelfde naam geven als de naam van de methode. In ons geval wordt de kattenboon " getCat
" genoemd. Maar omdat we de klasse gebruiken, niet de naam, om de cat bean in de methode te krijgen main
, is de naam van de bean niet belangrijk voor ons. Maak op dezelfde manier een hondenboon, rekening houdend met het feit dat Spring de methodenaam aan de boon zal geven. Om onze papegaaiboon expliciet een naam te geven, geven we eenvoudig de naam tussen haakjes aan na de @Bean
annotatie:
@Bean("parrot-polly")
public Object weNeedMoreParrots() {
return new Parrot();
}
Zoals je kunt zien, heb ik hier een Object
retourtype aangegeven en de methode een willekeurige naam gegeven. Dit heeft geen invloed op de naam van de boon, omdat we de naam hier expliciet hebben opgegeven. Toch is het beter om een min of meer betekenisvolle retourwaarde en methodenaam aan te geven. Doe dit alleen als u uzelf een plezier wilt doen als u het project over een jaar weer opent. :) Bekijk nu de situatie waarin we een boon nodig hebben om een andere boon te maken . Stel dat we willen dat de naam van de kat in de kattenboon de naam van de papegaai is plus de tekenreeks "-killer". Geen probleem!
@Bean
public Cat getCat(Parrot parrot) {
Cat cat = new Cat();
cat.setName(parrot.getName() + "-killer");
return cat;
}
Hier zal Spring zien dat om deze boon te maken, het raamwerk de eerder gemaakte papegaaiboon moet passeren. Dienovereenkomstig zal het de noodzakelijke keten van methodeaanroepen regelen: eerst wordt de methode voor het maken van papegaaien aangeroepen en vervolgens geeft het framework de nieuwe papegaai door aan de methode voor het maken van katten. Hier komt afhankelijkheidsinjectie om de hoek kijken: de lente zelf geeft de vereiste papegaaiboon door aan onze methode. Als IDEA boos wordt over de parrot
variabele, vergeet dan niet om het retourtype van de methode voor het maken van papegaaien te wijzigen van Object
naar Parrot
. Bovendien kunt u met de op Java gebaseerde configuratie absoluut elke Java-code uitvoerenin uw methoden voor het maken van bonen. Je kunt echt alles doen: andere hulpobjecten maken, andere methoden aanroepen, zelfs methoden die niet zijn gemarkeerd met Spring-annotaties, lussen maken, Booleaanse voorwaarden - wat er maar in je opkomt! Dit is niet allemaal mogelijk met automatische configuratie, en al helemaal niet met XML-configuratie. Laten we nu eens kijken naar een probleem dat een beetje leuker is. Polymorfisme en interfaces :) We maken een WeekDay
interface en creëren 7 klassen die deze interface implementeren: Monday
, Tuesday
, Wednesday
, Thursday
, Friday
, Saturday
, Sunday
. We zullen de interface een String getWeekDayName()
methode geven, die de naam van de dag van de week voor de overeenkomstige klas zal retourneren. Met andere woorden, de Monday
klas zal terugkeren "Monday
", enz. Stel dat het bij het starten van de applicatie onze taak is om een bean die overeenkomt met de huidige dag van de week in de context te plaatsen. Geen bean voor alle klassen die de interface implementeren - alleen de ene bean die we nodig hebben. U WeekDay
kunt doe dat ongeveer zo:
@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();
}
}
Hier is het retourtype onze interface. De methode retourneert een bonafide object van een van de klassen die de interface implementeren, afhankelijk van de huidige dag van de week. Nu kunnen we het volgende doen in de main()
methode:
WeekDay weekDay = context.getBean(WeekDay.class);
System.out.println("Today is " + weekDay.getWeekDayName() + "!");
Voor mij vertelt het programma me dat het zondag is :) Ik ben ervan overtuigd dat als ik het programma morgen uitvoer, de context een heel ander object zal bevatten. Merk op dat we de bean gewoon krijgen met behulp van de interface: context.getBean(WeekDay.class)
. Spring zoekt in zijn context naar de bean die de interface implementeert en geeft deze terug. Dan blijkt dat onze WeekDay
variabele eindigt met een zondagsobject, en het bekende concept van polymorfisme is van toepassing terwijl we met deze variabele werken. :) Nu een paar woorden over de gecombineerde aanpak , waarbij sommige bonen automatisch worden gemaakt door Spring, sommige door pakketten te scannen op klassen met de @Component
annotatie, en andere door op Java gebaseerde configuratie. Terwijl we dit overwegen, keren we terug naar de originele versie, waar de Cat
, Dog
, enParrot
klassen werden gemarkeerd met de @Component
annotatie. Stel dat we bonen willen aanmaken voor onze dieren door Spring automatisch de entities
verpakking te laten scannen, maar we willen ook een boon aanmaken met de dag van de week, zoals we net deden. Het enige wat u hoeft te doen is de @ComponentScan
annotatie toe te voegen op het niveau van de MyConfig
klasse, die we aangeven bij het maken van de context in main()
, en tussen haakjes het pakket aangeven dat moet worden gescand en automatisch bonen van de benodigde klassen maken:
@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();
}
}
}
Bij het maken van de context ziet Spring dat het de klas moet verwerken MyConfig
. Het gaat de klasse binnen en ziet dat het het pakket " " moet scannen en.codegym.info.fatfaggy.animals.entities
en bonen van die klassen moet maken, waarna het de methode MyConfig
van de klasse uitvoert en een bean aan de context toevoegt. In de methode hebben we nu toegang tot alle bonen die we nodig hebben: zowel dierlijke voorwerpen als een boon met de dag van de week. Als je ooit moet zorgen dat Spring ook enkele XML-configuratiebestanden ophaalt, kun je zelf zoeken op internet om een verklaring te vinden :) Samenvatting:getDay()
WeekDay
main()
- Probeer automatische configuratie te gebruiken
- Geef tijdens de automatische configuratie de naam op van het pakket dat de klassen bevat waarvan de bonen moeten worden gemaakt
- Deze klassen zijn gemarkeerd met de
@Component
annotatie - Spring doorloopt al deze klassen, maakt objecten en plaatst ze in de context;
- Als automatische configuratie om de een of andere reden niet bij ons past, gebruiken we op Java gebaseerde configuratie
- In dit geval maken we een gewone Java-klasse waarvan de methoden de objecten retourneren die we nodig hebben. We markeren deze klasse met de
@Configuration
annotatie als we het hele pakket gaan scannen in plaats van een specifieke klasse aan te geven bij de configuratie bij het maken van de context - De methoden van deze klasse die bonen retourneren, zijn gemarkeerd met de
@Bean
annotatie
@ComponentScan
annotatie.
GO TO FULL VERSION