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:
ByteArrayPropertyEditor
CharacterEditor
CurrencyEditor
CustomBooleanEditor
CustomDateEditor
FileEditor
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:
StaticMessageSource
ResourceBundleMessageSource
ReloadableResourceBundleMessageSource
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
ApplicationEvent
osztályból! - Az esemény kiváltója injektáljon egy
ApplicationEventPublisher
objektumot! - Az eseménykezelő implementálja az
ApplicationListener
interface-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:
FileSystemResource
ClassPathResource
UrlResource
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):
1
export 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:1
spring.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
-P
paramétert. Példuál "prod" package előállításához:1
mvn 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) AzEnvironment
injektá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.active
property 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 (
IllegalArgumentException
keletkezik). 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.