Spring Konfiguráció részletesen¶
Ebben a fejezetben a Spring nyújtotta konfigurációs lehetőségekkel ismerkedünk meg mélyrehatóan.
Property Editors¶
A PropertyEditor interface lehetővé teszi, hogy egy property értékét String-be és String-ből konvertáljuk.
Ez azért fontos témakör, mivel a legtöbb konfiguráció, melyet írunk alapvetően String típusban fogalmazódik meg (pl.: property fájlban definiált property-k).
A Spring által adott PropertyEditor-ok a spring-beans modulon belül találhatóak meg, de fontos lehet tudni, hogy maga a PropertyEditor nem a Spring által kitalált dolog, hanem a JavaBeans API része.
Itt sok-sok beépített PropertyEditor-t találhatunk, mint például:
ByteArrayPropertyEditorCharacterEditorCurrencyEditorCustomBooleanEditorCustomDateEditorFileEditor
csak hogy néhányat említsünk.
Példa a dátum konverziók megadásához:
1 2 3 4 5 6 7 8 9 | |
A fenti kódot a bean-ünknél elhelyezve máris aktiválódik a konverziós logika.
A dátum alapú PropertyEditor-ok alapból nincsenek bekapcsolva, de a többi igen.
A fenti kód a CustomDateEditor-t használta.
Amennyiben saját custom property editort szeretnénk létrehozni, akkor a PropertyEditorSupport osztályból kell származnia (getAsText() és setAsText() metódusokat kell implementálnia) a saját editornak és az osztály nevének az Editor postfix-szel kell végződnie!
A PropertyEditor-ok használatát részletesebben lásd itt.
Az ApplicationContext további képességei¶
Az eddig ismertetett képességek leginkább a BeanFactory érdemei voltak.
Az ApplicationContext azonban további feature-öket is szolgáltat, mint például:
- Többnyelvűség
- Esemény publikálás
- Erőforráskezelés
- További életciklus interfészek
- Továbbfejlesztett automatikus konfiguráció (infrastrukturális komponensekre)
Többnyelvűség (i18n)¶
A többnyelvűség támogatásához az ApplicationContext implementálja a MessageSource interface-t, mely segítségével az alkalmazásunk képes lesz hozzáférni String alapú erőforrásokhoz, melyeket üzeneteknek (message) nevezünk, és melyek több nyelvi megfelelőt adnak meg.
A többnyelvűség támogatásakor üzenetek listáját tároljuk, melyek kulcsokban adják meg az adott nyelvi megfelelőt.
Ugyan a többnyelvűséghez nem lenne szükséges az ApplicationContext használata, de mivel az nagyban segíti az üzenetek betöltését és azok elérését alkalmazás szerte, így ajánlott a használata.
A Spring 3 MessageSource implementációt kínál a számunkra:
StaticMessageSourceResourceBundleMessageSourceReloadableResourceBundleMessageSource
A StaticMessageSource használata nem javasolt éles környezetben, hiszen kívülről nem konfigurálható (általában erre pedig szükség van).
A ResourceBundleMessageSource az üzenetek betöltését a ResourceBundle által végzi.
A ReloadableResourceBundleMessageSource ugyanerre képes, de támogatja a forrásállományok ütemezett újratöltését is.
A fenti implementációk mindegyike implementálja a HierarchicalMessageSource interface-t is, mely segítségével a MessageSource példányokat egymásba is ágyazhatjuk, mely az egyik kulcsa annak, ahogy az ApplicationContext kezeli a MessageSource példányokat.
A Spring Boot tovább egyszerűsíti a többnyelvű alkalmazásunk írását, melyről a gyakorlati anyagok között lesz részletesen szó.
Alkalmazás események¶
Az ApplicationContext támogatja az események publikálását és kezelését is, melyben ő maga a bróker szerepét tölti be (ő regisztrálja az eseményeket és továbbítja a megfelelő helyekre, hogy ott kezelhessük azokat).
Az alapszabályok:
- Az események származzanak az
ApplicationEventosztályból! - Az esemény kiváltója injektáljon egy
ApplicationEventPublisherobjektumot! - Az eseménykezelő implementálja az
ApplicationListenerinterface-t!
Az egyedi események alapértelmezetten szinkron módon jönnek létre.
Nézzünk is egy példát egy egyszerű eseményre:
1 2 3 4 5 6 7 8 9 10 11 | |
Az üzenet kiváltója létrehoz egy CustomEvent objektumot, majd publikája, hogy minden érdeklődő (listener) megkaphassa azt.
Ehhez injektáljunk a bean-be egy ApplicationEventPublisher objektumot, melynek publishEvent() metódusával publikálhatjuk az eseményt!
1 2 3 4 5 6 7 8 9 10 11 | |
Megjegyzés
Alternatívaként a bean implementálhatja az ApplicationEventPublisherAware interface-t.
Az eseménykezelőnek implementálnia kell az ApplicationListener interfészt, melynek az onApplicationEvent() metódusát kell kifejtenünk!
Az eseménykezelő metódus generikus paramétert vár, így jelen esetben CustomEvent-et.
1 2 3 4 5 6 7 8 9 | |
Mivel az ApplicationEventPublisher-ben létrejövő üzenet szinkron, így a publishCustomEvent() metódus futása egészen addig blokkolt, ameddig minden eseménykezelő le nem kezeli az eseményt.
Megjegyzés
Spring 4.2-től az eseménykezelőknek nem kell implementálnia az ApplicationListener interfészt, hanem elhelyezhetjük a @EventListener annotációt is a megfelelő metóduson.
Ilyenkor figyeljünk a paraméter típusára, illetve arra, hogy a metódus láthatósága public legyen!
Erőforrások kezelése¶
A Springben sokszor kell erőforrás fájlokhoz hozzáférnünk (például konfigurációs adatok, képek). A Spring egy egységes mechanizmust biztosít az erőforrások kezelésére, mindezt protokoll-független módon (egy fájlt a lokális fájlrendszerről ugyanúgy kezelhetünk, mint egy távoli fájlt, vagy a classpath-ban lévő fájlt).
Az egységes kezelés megvalósításának egyik alappillére a org.springframework.core.io.Resource interfész, mely többek között a következő funkcionalitásokat adja:
contentLength()exists()getDescription()getFile()getFileName()getURI()getURL()isOpen()isReadable()lastModified()
A Resource interfészhez több implementációt is találhatunk:
FileSystemResourceClassPathResourceUrlResource
A Spring keretrendszer a Resource példányokat nem direktben hozza létre, hanem a ResourceLoader interfészt használja, illetve annak alapértelmezett megvalósítását: DefaultResourceLoader,.
Amikor mi magunk akarunk erőforrásokat kezelni, akkor még csak nem is ezt ajánlott használnunk, hanem magát az ApplicationContext-et!
Példa:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
A fenti példában minden getResource() hívásban egy-egy URI-t adunk át, melyeknél a file: és a http: protokollt is használtuk., azonban a classpath: prtokoll megadás egy Spring specifikus megadás, melynek hatására a kért erőforrást a classpath-ban keresi a rendszer.
A Resource-ok injektálását a @Value is támogatja:
1 2 | |
Profilok¶
Amennyiben definiáltuk a profilhoz tartozó beállításokat, akkor nincs más dolgunk, mint aktiválni valamelyik profilt. Ezt többféleképpen is megtehetjük:
WebApplicationInitializer-en keresztül:1 2 3 4 5 6 7 8 9 10
@Configuration public class MyWebApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { servletContext.setInitParameter( "spring.profiles.active", "dev"); } }
ConfigurableEnvironment-en keresztül1 2 3 4
@Autowired private ConfigurableEnvironment env; ... env.setActiveProfiles("dev");- Context paraméter a
web.xml-ben1 2 3 4
<context-param> <param-name>spring.profiles.active</param-name> <param-value>dev</param-value> </context-param> - JVM System Parameter
1-Dspring.profiles.active=dev - Környezeti változó (Unix):
1export spring_profiles_active=dev - Maven profile:
Ebben az esetben az1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<profiles> <profile> <id>dev</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <spring.profiles.active>dev</spring.profiles.active> </properties> </profile> <profile> <id>prod</id> <properties> <spring.profiles.active>prod</spring.profiles.active> </properties> </profile> </profiles>application.properties-be átvezethetjük az aktív maven profile-t, ha a következőt használjuk:1spring.profiles.active=@spring.profiles.active@de ehhez az is kell, hogy aktiváljuk a
resource filtering-et:
Ez a változók behelyettesítése miatt szükséges.1 2 3 4 5 6 7 8 9
<build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> ... </build>A package előállításakor ilyenkor használhatjuk a
-Pparamétert. Példuál "prod" package előállításához:1mvn clean package -Pprod
@ActiveProfile: tesztek írásakor használható
Bármelyik beant, amely nem tartozik egyetlen profilhoz sem, az alapértelmezett (default) profilhoz rendeljük hozzá.
Ha nincs aktív profil kiválasztva, akkor az alapértelmezettet használja a rendszer, de az alapértelmezést meg is lehet változtatni spring.profiles.default property beállításával.
Arra is lehet igény, hogy az éppen aktív profilt forráskódból elérhessük. Erre is több módszert lehet használni:
Environment(a követekző alfejezetben mutatjuk meg azEnvrionment-et) AzEnvironmentinjektálása után az rendelkezésre bocsájtja a profilokat, melyekete aenvironment.getActiveProfiles()hívással kérhetünk le.spring.profiles.active: A kiválasztott profil nevét aspring.profiles.activeproperty injektálásával is megkaphatjuk:1 2
@Value("${spring.profiles.active}") private String activeProfile;Amennyiben több aktív profil is van, akkor ezeket vesszővel elválasztva adja vissza a rendszer. A fenti kódnál azonban probléma fog fellépni (a context-et nem tudja betölteni a rendszer), ha nincs megadva aktív profil (
IllegalArgumentExceptionkeletkezik). Ennek kezelésére használjuk a már bemutatott default érték használatát:1 2
@Value("${spring.profiles.active:}") private String activeProfile;A kettőspont után most nincs semmi, így üres string-et kapunk eredményül.
Spring Boot esetén az eddig ismertetett profil megoldások mind használhatóak, illetve még ennél több is.
A spring.profiles.active property-t használhatjuk az property állományunkban (alapértelmezetten az application.properties-ben), hogy megadjuk a használni kívánt profilt.
A legfontosabb újítás azonban a profil-specifikus property fájlok használata. Fontos a nevezéktan, amelyet használnunk kell:
1 | |
Abban az esetben, ha profil specifikus property állományunk is van, akkor a Spring betölti az alap application.properties állományt, illetve az aktív profilokhoz tartozó property állományokat is.
Environment és PropertySource¶
Az Environment interface egy absztrakciós szint, ami segíti a környezeti információk elérését az éppen aktuálisan futó Spring alkalmazásban.
Ahogy azt az előző alfejeztben láthattuk az Environment segítségével elérhetjük az aktív profilt is, de segítséget nyújt abban is, hogy az összes property-t (pl.: az alkalmazás mappája, adatbázis csatlakozási információk, stb.) ezen keresztül érjük el.
Az Environment interface egységes formába gyúrja a rendszer property-ket, környezeti változókat és az alkalmazás property-jeit, azaz ezeket mind elérhetjük az Environment-ből.
A PropertySource interface a kulcs=érték alakú állományok reprezentálását segíti, lehet az egy Properties típusú objektum, vagy egy Map vagy bármi egyéb.
Nézzünk is egy példát:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Kimenet
1 2 3 4 5 6 | |
Az ApplciationContext-től el tudjuk kérni a hozzá tartozó Environment-et (ConfigurableEnvironment implementáció használható).
A ConfigurableEnvironment-től el is kérhetjük a PropertySources objektumot, melyen keresztül módosíthatjuk is a property-ket, vagy akár sajátot is hozzáadhatunk.
Példa:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Kimenet
1 2 | |
A fenti kódban az Environment-től elkérjük az összes PropertySource-ot, melyet általában egy PropertySources interfész mögé szokás rejteni (a MutablePropertySources egy alapértelmezett implementációja).
Ezután egy Map alapú property megadással bővítjük a PropertySources tárházat.
Ha egy property többféleképpen is meg van adva akkor ezek feloldási sorrendje:
- Rendszer property-k (aktuális JVM-re)
- Környezeti változók
- Alkalmazás által definiált property-k
Ennek tükrében, például a user.home környezeti változót hiába írnánk felül az általunk adott Map-ben, a rendszer akkor is a System property-k közül venné azt.
Azonban a sorrend megváltoztatható:
1 | |
1 | |
hívást!
Ilyenkor a listában hamarabb fog szerepelni az általunk definiált test_prop_source PropertySource, így abban keres először a rendszer.
Ezen háttérismeretek után nézzük meg, hogy még magasabb absztrakciós szinten, hogyan használhatjuk a property-ket!
A property-ket általában jó ha kiszervezzük egy külső állományba (például egy *.properties állományba).
Az alkalmazásunk nagy valószínűséggel tartalmazni fog egy Java konfigurációs fájlt, melyre minden további nélkül rárakhatjuk a @PropertySource annotációt, melynek hatására a property fájlt betölti a rendszer és hozzáadja az aktuális az Environment-hez.
1 2 3 4 5 | |
Egyszerre akár több PropertySource is megadható (mivel rajta van a @Repeatable annotáció; csak 8-as Java-val használható):
1 2 3 4 5 6 | |
Ezzel egyenértékű a következő megadás, mely nem korlátozódik 8-as Java-ra:
1 2 3 4 5 6 7 8 | |
Property ütközéskor a legutolsónak megadott PropertySource élvez előnyt.
Megjegyzés
Ne felejtsük el, hogy Spring Boot-ban az alapértelmezett property állomány az application.properties!!!
Teszteléshez a Spring Boot az src/test/resources-ba helyezett property fájlokat is betölti, így felülírva az alapértelmezett application.properties-ben megadott értékeket.