spring boot
    
    Tartalomjegyzék
    
    
    alapok
    
      - 
        Program módosítás frissítése
 A pom.xml-be szükséges egy bejegyzés, hogy ne kelljen program módosításkor az alkalmazást leállítani és újraindítani:
        
        Továbbá a netbeans-ben a project properties menünél: compile: compile on save: yes
- 
        No war!
 Ne készítsünk war fájlt. A spring boot arra lett kitalálva hogy a jar fájlt futtassunk, 
        melyet nem kell telepíteni alkalmazás szerverre.
 Ha mégis war fájlra lenne szükség:
        war készítése
- 
        Futtatás "java -jar" módon
 A pom.xml-be be szükséges a "spring-boot-maven-plugin":
        
        Ez a plugin arra szolgál, hogy a project build-nél előállít egy nagy méretű, kb. 20 Mbyte-os jar-t is (fat jar),
        ami az összes függőséget tartalmazza, tehát tartalmazza a spring-es jar-okat is. 
        Az eredeti jar is megmarad egy ".jar.original" kiterjesztéssel.
        A "fat jar" viszont így már önmagában képes futni:
        
        Dokumentáció
- 
        Jar fájl futtatása esetén System.exit() metódussal leállítható az egész webalkalmazás, 
        pont úgy mint egy sima java application esetén.
      
probléma, megoldás
    scanBasePackages
    
      - 
        Látszólag minden jól van beállítva a programban, de futtatásnál beállítási hiányosságokat jelez a spring.
      
- 
        Megoldás: ellenőrizzük, hogy az alkalmazást futtató osztályban (pl. Run.java), 
        jól van-e beállítva az a package, mely alatt a spring a bean-eket keresi.
 Tehát ennek az annotációnak a helyes beállítása alapvető:
Can't call url
    
      - 
        A log-ok alapján látszólag elindult az alkalmazás, a log nem tartalmaz hibaüzenetet,
 azonban az alkalmazás egyetlen url-je sem hívható, mintha el sem indult volna.
- 
        Lehetséges, hogy az alkalmazás mégsem indult el teljesen. Például tartalmaz egy event listener-t,
        ami adatbázis műveletet végez, az pedig végtelen ideig egy adatbázis lock miatt várakozik.
        Ezért nincs hibaüzenet a log-ban. Csak az event listener után indul az embedded tomcat, ezután hívható
        az alkalmazás. Ha a tomcat elindult, azt mindenképp látjuk a log-ban ebben a formában:
           
        Sőt, a log végének tartalmaznia kell az elindítás idejét is, például: 
        
        vagy
        
        Így tehát, a log-ot megvizsgálva következtethetünk a problémára.
      
Circular reference
          
      - 
        Nem indul el az alkalmazás, a következő hiba keletkezik:
        
        Tehát látszólag a spring-bean-ek körbehivatkozzák egymást, és a spring nem tudja hogy hozza őket létre. 
      
- 
        Megoldás: a @Lazy annotációval jelezzük a spring-nek, hogy csak később hozza létre azt a bean-t 
        amelyikkel probléma van:
        
      
StackOverflowError
    
      - 
        Ennek több oka lehet. Lehetséges, hogy egy java metódusban exception keletkezik, és az AOP be van állítva
        ennek a hibának a kezelésére. Azonban az aop-metódus is meghívja ezt a hibás metódust, 
        melyben ismét exception keletkezik, és ismét az aop-metódusra kerül a vezérlést ...stb. 
        A végén a kimeneten csak StackOverflowError keletkezik, a valódi hiba rejtve marad.
      
- 
        Megoldás: 
        az AOP-ban a hiba kezelő metódus legelőszőr a LOG-ba írja bele a hibát, így a hiba biztos látszódni fog. 
      
Array binding
    
      - 
        Html űrlap esetén ha egyetlen input mező tartalmát tömbként, list-ként fogadja a form objektum, 
        és mező tartalmaz vessző karaktert, akkor a spring az adatot szétszedi a vesszők mentén több tömbelemre,
        az adat nem marad egyben. Gyakorlatilag ez akkor fordul elő, amikor ugyanolyan néven több checkbox van,
        de csak egyetlen van kipipálva, és annak value-ja ráadásul vesszővel elválasztva tartalmaz adatot.
      
- 
        Megoldás: form objektumban tömböt, list-et csak checkbox-ok esetén alkalmazzunk, abban az esetben ha a megjelenő 
        checkboxok számát a model szabályozza. Továbbá a checkboxok value attribútuma semmiképp se tartalmazzon 
        vessző karaktert.
      
- 
        Működés: a text ➔ String[] konverziót a StringArrayPropertyEditor osztály valósítja meg, 
        a konstruktorban megadható más karakter, vagy akár null is:
        
        Sajnos arra nem ad megoldást amikor text ➔ List<String> konverzió történik.
 Ezekre az esetekre saját PropertyEditor-t kell fejleszteni.
Model has no value for key
    
      - 
         A model has no value for key hibaüzenet esetén a stack trace nem tartalmaz általunk írt java sorban
         keletkező hibát. Ebből arra lehet következtetni, hogy a hiba nem az általunk írt java kódban van.
         Ilyen eset amikor egy controller nem a megfelelő query paraméterrel van meghívva, de ilyen eset az is
         amikor a kontroller lefut, de a választ nem tudja a spring feldolgozni. Ezesetben pont az utóbbi fordul elő:
        
        Ugyanis a spring okoskodik, és ha az url-ben kapcsos zárójelet talál, akkor megpróbálja feloldani a modelben
        található kulcs alapján.
 Ha pedig nem talál ilyet, akkor exception keletkezik ezzel a hibaüzenettel:
@ApplicationScope
    
      - 
        Application scope bean létrehozása, mely ezzel egyenértékű:
        
      
- 
        Ennek az annotációnak a használata nem szükséges, ha elhagyjuk, akkor is egyetlen példány lesz a spring bean-ből. 
      
- 
        Az alkalmazás indulásakor a spring framework az összes application scope bean-t automatikusan létrehozza, 
        tehát nem várja meg hogy hivatkozzon rá valami eljárás. 
        Egy adott application scope bean-nél ezt úgy lehet megakadályozni, hogy ellátjuk
        @Lazy annotációval is. Ebben ez esetben a bean csak akkor fog létrejönni amikor megtörténik a rá való
        hivatkozás a futó programkódban.
        A session, request, prototype bean-ek viszont nem jönnek létre automatikusan a program indulásakor,
        csak amikor a futó programkód hivatkozik rájuk.
      
@Autowired
    
      - 
        Az @Autowired annotáció csak spring bean-en belül működik.
      
- 
        2 spring bean összekötésére szolgál. Bármilyen scope-nál működik. 
        Ha application scope bean (A) hivatkozik request scope bean-re (R) akkor minden request-nél 
        más-más (R) bean jön létre (A) bean-ben. 
        Ez akkor is működik ha a konstruktornál használjuk az @Autowired annotációt.
      
- 
        El lehet vele érni olyan objektomokat is, amit nem mi állítottunk be spring bean-nek, hanem a keretrendszer:
        
      
- 
        Ha már egy bean konstruktorában hozzá akarunk férni másik bean-hez akkor a konstruktor szintjén kell 
        definiálni az @Autowired-t.
        (Különben null értéket kapnánk a bean-re hivatkozva)
        
      
- 
        Működés: az @Autowired annotációval nem a megnevezett osztály egy példányát kapjuk meg, 
        hanem egy BEAN PROXY-t, lásd: 
        
        így valószínű hogy egy request scope bean (R) esetén is csak 1 bean proxy példány van, és ennek metódusát hívva,
        más-más bean-hez delegálja tovább a kérést.   
      
- 
        Bean nevesítés:
 Akkor hasznos ha ugyanolyan típusú spring bean-ből több van, és így a típus alapján a spring nem tudná 
        meghatározni melyik bean-re hivatkozunk.
 Ekkor a @Qualifier annotációt is alkalmazni kell:
@Bean
    
      - 
        Metódusra alkalmazható annotáció egy konfigurációs osztályon belül. 
        A metódus által visszaadott objektum példány spring bean lesz.
 A spring bean default neve a metódusnév lesz.
- 
        A default bean név helyett megadható egy másik név:
        
      
@Component
    
      - 
        Ez egy régi annotáció, mely helyett inkább ezek egyikét használjuk:
        
      
@Configuration
    
      - 
        Osztályra alkalmazható annotáció, az osztályból konfigurációs osztály lesz, mely spring bean. 
      
@ConfigurationProperties
    
      - 
        A @ConfigurationProperties annotációt használhatjuk például egy DataSource-t visszaadó metódusra,
 jelezvén hogy milyen jellemzőket kell beolvasni a DataSource felépítéséhez.
- 
        A @ConfigurationProperties annotációt használhatjuk spring-bean osztályra is,
 amikor az osztály tagjai az application.yml egy-egy jellemzőjének felelnek meg (CP-osztály).
- 
        A @ConfigurationProperties paramétereként megadható egy prefix, melyen belül olvassuk a jellemzőket,
 így általában az application.yml csak egy részét használjuk.
- 
        A CP-osztály akkor is hasznos, ha az application.yml-ből összetett típusba (List, Map) 
        szeretnénk beolvasni, mert erre a @Value nem alkalmas.
 application.yml:
        
        CP-osztály:
- 
        Ha a CP-osztályban sima Map-et definiálunk statikus típusként, 
        akkor a spring LinkedHashMap-et használ dinamikus típusként,
        így a map kulcsainak sorrendje megegyezik a yaml fájlban definiált kulcsok sorrendjével!
      
- 
        A CP-osztály tagjai @Value annotációval is elláthatók, így hozzáférhetünk bármelyik application.yml jellemzőhöz,
        még azokhoz is amelyek nem a prefix-en belül vannak.
      
- 
        A CP-osztályban beállított tagok lehetnek null értékűek is, ha az application.yml nem tartalmaz ilyen jellemzőt.
 Ez a probléma kiküszöbölhető, ha az osztály tagjaira validáció van előírva.
- 
        Ha a CP-osztályban String-be olvasunk be adatot, és az application.yml megfelelő tagja null, akkor a CP-osztályban
        ez nem null lesz, hanem üres String.
      
- 
        Validáció
 
          - 
            pom.xml kiegészítése:
                
          
- 
            A CP-osztály ellátása @Validated annotációval (org.springframework.validation.annotation package)
          
- 
            A CP-osztály kívánt tagjainak ellátása valamelyik annotációval: 
            @NotNull, @NotEmpty, @Size, ...
            (javax.validation.constraints package)
          
 
- 
        Baeldung példa
      
@Controller
    
      - 
        Osztályra alkalmazható annotáció, az osztályból controller lesz, mely spring bean.
      
- 
        Az osztályt nem szokás ellátni scope-annotációval, így egyetlen példány lesz belőle.
      
- 
        A spring bean default neve az osztálynév lesz kis kezdőbetűvel.
      
- 
        A default bean név helyett megadható egy másik név, hiszen egy projektben lehetséges hogy például 
        van két HelpController nevű osztály, melyeknek ugyanaz lenne a nevük, ami nem megengedett. Ennek elkerülése: 
        
      
@ControllerAdvice
    
      - 
        Controllereknek adott javaslatok beállítása.
      
- 
        Például az űrlapmezők editorának beállítása, mely kiterjed a teljes alkalmazásra:
             
      
@CookieValue
    
      - 
        Ezzel az annotációval lehet beolvasni egy cookie értékét, például egy controller metódusban.
      
- 
        Alapesetben a beolvasott cookie-nak léteznie kell, különben exception keletkezik.
        Ha nem kötelező léteznie, akkor állítsuk be a required = false paramétert, így ha a cookie nem létezik,
        akkor null érték kerül a változóba, és exception sem keletkezik.
      
- 
        Példa a "JSESSIONID" beolvasására (a kis és nagybetűk számítanak). 
        Ha nincs ilyen cookie akkor default értékként "?" -et vesz fel a változó:
        
        Példa a "teszt" nevű cookie beolvasására, a spring itt adatkonverziót is végez. 
        Ha a cookie nem létezik akkor a "t" változóba null kerül:
        
      
- 
        Cookie írására a spring nem ad speciális módszert, hanem elkérve a HttpServletResponse objektumot, 
        ahhoz kell a cookie-t hozzáadni:
        
      
- 
        JSESSIONID cookie
 Ennek tartalma megegyezik a HttpSession.getId() értékkel. Azonban ha egy webalkalmazást újratelepítünk, 
        akkor hiába marad meg a JSESSIONID cookie a web böngészőben, és küldi fel a következő http-kéréskor 
        a webalkalmazásnak, az egy érvénytelen JSESSIONID-nek érzékeli, és új értékkel írja felül.
@CrossOrigin
    
      - 
        A cross origin engedélyezést lehet megvalósítani ezzel az annotációval. 
      
- 
        A @CrossOrigin annotáció metódusra, vagy egy egész osztályra is alkalmazható.
      
- 
        Ha a hívás mindenhonnan engedélyezett, az többféleképpen is írható:
        
      
- 
        A hívás csak az adott helyekről engedélyezett:
        
      
- 
        Globálisan is beállítható a cross origin az egész alkalmazásra.
 Egy @Configuration annotációval ellátott osztályba, mely implementálja a WebMvcConfigurer-t, 
        el kell helyezni ezt a metódust:
        
        Így sehol nem kell használni a @CrossOrigin annotációt.
- 
        Figyelem!
 A cross origin miatt a programban lévő interceptorhoz befuthat OPTIONS típusú kérés is.
 Ezzel vizsgálja a kliens hogy a szerver hívható-e. A szerver a http válaszban jelzi, hogy engedélyezett-e 
        a hívás (ezt a spring végzi).
        Fontos hogy az interceptor ezeket a kéréseket ne akadályozza, ezért az interceptor preHandle() 
        metódusát ezzel kell kezdeni:
- 
        dokumentáció
      
@DateTimeFormat
    
      - 
        Form bean annotáció mellyel adott mezőre beállítható a dátum formázás.
      
- 
        Amennyiben egy globális initbinder-ben már beállítottunk formattert a Date adattípusra, 
        a @DateTimeFormat nem jut érvényre.
      
- 
        Példa a form bean egy tagjának formázására:
        
      
@DependsOn
    
      - 
        A @DependsOn annotációval beállítható, hogy a spring az annotációval megjelölt bean-t 
        csak az után hozza létre,
 miután az annotációban megadott nevű bean-eket már létrehozta.
- 
        Az elsoBean csak az után jön létre, hogy létrejött a masodikBean és a harmadikBean:
        
      
@EventListener
    
      - 
        Ha az alkalmazás indításával egyidőben szeretnénk programkódot futtatni, 
        akkor az @EventListener lehet rá megoldás.
      
- 
        Az event listener több különböző eseményre is ráköthető.
 Ilyen esemény a ContextRefreshedEvent, amikor már a spring context teljesen kész állapotban van, 
        viszont a http kiszolgáló még nincs aktiválva, vagyis a felhasználók még nem hívhatják meg az alkalmazást.
- 
        Egy másik esemény az ApplicationStartedEvent, amikor a spring alkalmazás már teljesen elindult, 
        a http kiszolgáló is aktiválva van.
      
- 
        A metódus nevek nem számítanak, csak a metódus paramétere dönti el milyen eseményre fog lefutni.
      
- 
        Az event listener használatára egy jó példa, amikor memória adatbázist használunk, és az alkalmazás 
        indulásakor szeretnénk felépíteni az adatbázis struktúráját, de amíg ez nincs kész, a felhasználók 
        nem hívhatják meg az alkalmazást. Az Application context beállítását is el lehet itt végezni.
        Továbbá kiírjuk a konzolra, hogy az alkalmazás 100%-ban elindult.
        
      
@GetMapping
    
      - 
        Rövidítése a következőnek:
        
      
@InitBinder
    
      - 
        Az @InitBinder annotációval megjelölt metódusban form bean, és http request paraméter-re vonatkozó 
        beállítások adhatók meg.
 Ilyen beállítás például converter, validátor hozzárendelése form bean-hez és request paraméterhez.
- 
        Ha az @InitBinder-ben nem adunk meg neveket, akkor mindenre vonatkozik.
      
- 
        Az @InitBinder megadható globálisan, vagyis minden controller-re vonatkozóan,
 ha a @Configuration és a @ControllerAdvice annotációval is ellátott osztályban alkalmazzuk.
- 
        Az @InitBinder megadható controller-re vonatkozóan, ha a controlleren belüli metódusra alkalmazzuk.     
      
- 
        @InitBinder példák egy controlleren belül. A form bean model attribútum neve legyen: "bean"
        
          - 
            A form bean 2 mezőjét máshogy formázzuk:
            
          
- 
            A form bean összes dátum mezőjét ugyanúgy formázzuk:
            
          
- 
            A form bean-hez standard spring validátort rendelünk.
 Ha elhagynánk a "bean" megnevezést akkor a controlleren belüli összes form bean-re érvényes lenne.
- 
            A "d" nevű request paramétert ÉÉÉÉ formában kapjuk http get-tel, és egy Date típusban fogadjuk:
            
            A konverzióhoz ez szükséges:
            
          
- 
            A http post request body részében json-t kapunk, melyet a MuveletJson osztályba konvertálunk:
            
            A MuveletJson osztályt szeretnénk validálni a MuveletValidator osztállyal:
            
          
 
- 
        Editorok regisztrálására az @InitBinder-ben:
        
      
@Lazy
    
      - 
        A @Lazy annotációval jelezhető, hogy valamilyen spring bean-t később hozzon létre a spring framework.
      
- 
        Ha egy @Configuration annotációval ellátott osztályt megjelöljük a @Lazy annotációval is,
        akkor az osztályban lévő @Bean-nel jelölt spring bean-ek is csak később jönnek létre, 
        amikor már futó programkód hivatkozik rájuk.
      
- 
        Ha egy @Component-nel ellátott osztályt ellátjuk a @Lazy annotációval is,
        és erre az osztályra hivatkozó @Autowired is el van látva a @Lazy annotációval,
        akkor ez a spring bean is csak később jönnek létre, amikor már futó programkód hivatkozik rá.
      
@ModelAttribute
    
      - 
        A @ModelAttribute két helyen használható: 
        
          - Controller metódus megjelölése.
- Controller metódus paraméterének megjelölése.
 
- 
        Controller metódus megjelölése:
 A @ModelAttribute -tal jelölt metódusokat a spring automatikusan mindig lefuttatja ha ehhez a controllerhez 
        kerül a vezérlés,
 méghozzá úgy, hogy a @ModelAttribute -tal jelölt metódusok futnak le legelőszőr, majd utána fut le a 
        @RequestMapping -gel jelölt metódus.
 A @ModelAttribute -tal jelölt metódusnak átadható paraméter is, például a @RequestParam, 
        vagyis ugyanazok amik átadhatók egy @RequestMapping -gel jelölt metódusnak is.
 
 A példában a metódus visszatérési értéke töltődik a model-be az annotációnál megadott névvel, 
        tehát a pontos idő kerül a model-be "datum" kulccsal:
 A @ModelAttribute -tal jelölt metódus több adatot is beállíthat a model-be. Ekkor nem kell semmit beállítani 
        a @ModelAttribute annotáció paramétereként, viszont a metódusba be kell kérni paraméterként a Model-t, 
        hogy ebbe több adatot is be lehessen állítani:
- 
        Controller metódus paraméterének megjelölése:
 Tipikusan form submit-ot fogadó metódusban fordul elő, amikor a model-be amúgy is bekerülő form bean model-ben 
        elfoglalt attribútum nevét módosítjuk ezzel az annotációval. Itt a model-be "bean" névvel kerül be az adatokkal 
        feltöltött form bean:
@PathVariable
    
      - 
        URL útvonalon lévő paraméter kezelésére szolgál.
      
- 
        A "required = false" beállítás nem működik, vagy nem úgy ahogy sejtjük.
        Ugyanis ha az url-ből kihagyjuk az értéket akkor a controller handler nem kezeli le, mivel nem ez az URL !
      
- 
        Példa:
        
      
@PostConstruct
    
      - 
        A @PostConstruct annotációval ellátott metódus azért előnyösebb mint a konstruktor, 
        mert ekkor már megtörtént a bean-ben a dependency injection. 
        A konstruktorban hiába hivatkozunk az @Autowired, vagy @Value által jelölt változókra, 
        azok még null értéket tartalmaznak.
      
- 
        Tehát egy bean létrehozásakor a spring lefuttatja a konstruktort, majd végrehajtja a dependency injection-t, 
        és utána lefuttatja a @PostConstruct annotációval ellátott metódus.
      
- 
        A bean életciklusa során csak egyszer hívódik meg a @PostConstruct annotációval ellátott metódus.
      
@PostMapping
    
      - 
        Rövidítése a következőnek:
        
      
@Primary
    
      - 
        A @Primary annotációt akkor érdemes használni, ha több azonos típusú spring-bean van.
 A @Primary abban segít, hogy ne kelljen minden spring-bean-re név szerint hivatkozni.
 Ugyanis ha nem használunk nevet (csak a típust), akkor a spring azt a bean-t használja amelyik a 
        @Primary annotációval van ellátva.
        Példa:
        
        Ezesetben a @Primary miatt az egyik bean-re úgy lehet hivatkozni, hogy nem adjuk meg a nevét, 
        a másikat pedig csak név alapján lehet elérni:
@Profile
    
      - 
        Program argumentumként vesszővel elválasztva megadható több aktív profile, például: 
        
        Ha ez meg van adva, akkor felülírja az application.yml-ben megadott értéket!
      
- 
        application.yml-ben megadva az aktív profile-ok: 
        
      
- 
        A @Profile annotációval lehet jelezni hogy az adott metódus vagy osztály csak akkor hajtódjon végre 
        ha a felsorolt profile-ok közül legalább egy aktív.
        Tehát ha a beállítjuk hogy az "xx" és "yyy" profile is aktív:
        
        akkor ezek a metódusok mind végrehajtódnak:
        
      
- 
        A @Profile a következőkkel együtt használható: 
        
      
- 
        Aktív profile-ok lekérdezése Environment-ből:
                
      
@Qualifier
    
      - 
        Spring bean-re való hivatkozásnál a bean megnevezésére szolgál, 
        az @Autowired annotációval együtt használatos.
 A használatát lásd az @Autowired-nél!
@Repository
    
      - 
        Ezt az annotációt olyan spring-bean-re kell alkalmazni mely adatbázis műveleteket végez, vagyis DAO osztályokra.
 Pontosabban kifejezi a spring-bean funkcióját, mintha a @Component-et alkalmaznánk rá.
@RequestBody
    
      - 
        A @RequestBody annotációval jelezhető, hogy a http request body egy változóba kerüljön.
      
- 
        A @RequestBody egyetlen paramétere a "required". 
        Default értéke true, így ha nem jön adat a http request body-ban, az exceptiont okoz.
      
- 
        Példa JSON fogadásra Map-be töltéssel: 
        
        Tudja fogadni még az összetettebb json struktúrákat is.
 Például: {"x" : 12, "y" : [1,2,3]} esetén a fogadoMap-be "y" kulcsnál egy List objektumot rak be.
 Például: {"x" : 12, "y" : {"aa" : 4000, "bb" : true}} esetén a fogadoMap-be "y" kulcsnál egy Map objektumot rak.
- 
        Példa JSON fogadásra, egy java osztállyal:
        
        Ha a json-ben egy várt adat hiányzik, akkor a fogadoJson megfelelő helyére null kerül.
 Ha a json több adatot tartalmaz, ezek figyelmen kívül lesznek hagyva.
 Eltérő adattípus exception-t okoz, de például a json-ben kapott string "13" -ast átkonvertálja, 
        ha a fogadoJson Integer típus-ba kéri.
- 
        Példa XML fogadásra, egy java osztállyal:
        
        az xml-t fogadó java osztály:
        
        mely fogadni tudja a következő xml-t:
        
        Feltétlenül szükséges az xml-t fogadó osztályban az @XmlRootElement(name = "..."), 
        ahol a "..." az xml gyökérelemének a neve.
 Ha az xml-ben egy várt adat hiányzik, akkor a fogadoXml megfelelő helyére null kerül.
 Ha az xml több adatot tartalmaz, ezek figyelmen kívül lesznek hagyva.
 Eltérő adattípus NEM OKOZ exception-t, a fogadoXml megfelelő helyére null kerül (mivel nincs xsd alapú ellenőrzés).
- 
        Példa BYTE-SOROZAT fogadására, byte-tömbbel:
        
        Bármilyen típusú adatot tud fogadni, nem okoz problémát az üres http request body sem.
      
      - 
        Http request header elemének változóba töltésére szolgál.
 Default-ban kötelező lennie a megadott header elemnek, de ez átállítható, 
        illetve beállítható egy default érték is. Példa:
@RequestMapping
    
      - 
        Egy controllerben lehet definiálni hogy milyen URL-re, request metódusra, url paraméterre, ... fusson le.
      
- 
        Osztály szinten, és metódus szinten is definiálható. Ha mindkét szinten definiálva van az url, 
        akkor ezek egybefűzésekor fut le a metódus.
      
- 
        Egyszerre több URL-t is be lehet állítani. Például megvalósítható a "home page" 
        kezelése is a "/" URL kezelésével:
        
      
- 
        A @RequestMapping url leíró stringjében a következő speciális leírók lehetnek:
        
      
- 
        Példa: controller osztály:
        
      
- 
        Példa:
        
        Fogadja a következő url-eket:
        
        Nem fogja el a következő url-eket:
        
      
- 
        Példa: Fogadja azokat az url-eket ahol a "home" és az "xy" között csak egyetlen elem van:
        
      
- 
        Példa: Fogadja azokat az url-eket ahol a "home" és az "xy" között tetszőlegesen sok elem van.
        
      
- 
        method
 Szűrni lehet vele hogy milyen http kérés típusra fusson a controller metódus, például:
- 
        params
 Szűrni lehet vele hogy milyen paraméterek esetén fusson a controller metódus.
 Például csak akkor fusson ha kap "x" ÉS "y" paramétert is:
        
        A params értékeit lehet tagadni is, lásd api doc.
- 
        produces
 Be lehet állítani vele a kimenet típusát, mely a http válasz fejlécében a "Content-Type" 
        értékeként megjelenik.
 Az érték megadható konkrét string-ként, vagy használható a MediaType osztály:
- 
        consumes
 Szűrni lehet vele a fogadott http kérés "Content-Type" típusát.
 Csak azokat a http kéréseket fogadja melynek a header-jében a "Content-Type" érték megegyezik a 
        consumes-nél felsorolt egyik értékkel, például:  
        
        A consumes értékeit lehet tagadni is, lásd api doc.
- 
        headers
 Szűrni lehet vele, hogy http kérés esetén milyen http header előfordulása esetén fusson a controller.
 Például csak akkor, ha a header-ben szerepel a "Connection: keep-alive":
        
        A headers értékeit lehet tagadni is, lásd api doc.
@RequestParam
    
      - 
        URL-ben kapott paraméterek fogadása egy controller metódusban.
      
- 
        Attribútumok:
        
          - 
            required: boolean
 Alapértelmezett érték: true
 True esetén megköveteli, hogy az url-ben szereplő érték java változóba konvertálásakor
            ne legyen a java változó értéke null.
 Tehát függ a programban beállított konverziótól (PropertyEditor, Converter).
 Ha nem teljesül a feltétel, akkor exception keletkezik.
- 
            defaultValue: String
 Ha az url-ben lévő érték java változóba konvertálásakor null vagy üres string kerülne, akkor helyette
            a java változóba ez a default érték kerül.
 Ez az érték a required által már nem lesz ellenőrizve, tehát ezek egyike is lehet:
 
- 
        Kötelező paraméter esetén
 Ha a paraméter nem szerepel az url-ben, és a defaultValue sincs megadva, akkor Exception keletkezik.
- 
        Nem kötelező paraméter esetén
 Ha a paraméter nem szerepel az url-ben, és a defaultValue nincs megadva, akkor az Object leszármazott 
        változóba a spring null értéket tesz.
- 
        Ha az url paramétert vele megegyező nevű java változóba fogadjuk, akkor használható a rövidebb forma:
        
      
- 
        Ha az url paramétert más nevű java változóba fogadjuk, akkor csak a hosszabb forma használható:
        
      
@RequestScope
    
      - 
        Request scope bean létrehozása, mely ezzel egyenértékű:
        
      
@ResponseBody
    
      - 
        Ezzel az annotációval elérhető hogy a controller metódus által visszaadott objektum ne megjelenítendő 
        view-ként legyen értelmezve, hanem ez az objektum kerüljön ki a kimentre.
      
- 
        Osztályra és metódusra is használható.
      
- 
        Példa:
        
      
- 
        Ha a controller metódus nem String típussal tér vissza akkor a "produces" határozza meg milyen 
        formátumra kell alakítani a kimenetet. 
        Például egy json kimenet előállítása:
        
      
- 
        Tetszőleges bináris kimenet előállítása, ahol nincs a metódusnak visszatérési értéke, hiszen közvetlenül 
        kezeljük a HttpServletResponse objektumot:    
        
      
- 
        El lehet érni, hogy mégis egy html oldal legyen a kimenet, de ekkor a html oldal nevét nem string-ként kell 
        visszaadni, hanem az alábbi módon: 
        
        A model beállítása a szokásos módon történhet.
      
- 
        Szintén el lehet érni, hogy mégis inkább egy redirect történjen:
        
      
@ResponseStatus
    
      - 
        A @ResponseStatus annotációval beállítható, hogy egy controller metódus a http válaszban milyen 
        státusz kódot adjon, például:
        
      
@RestController
    
      - 
        Olyan @Controller, melynek minden metódusára a @ResponseBody vonatkozik. 
      
@Scheduled
    
      - 
        Időzítéssel lehet elérni, hogy valamelyik bean valamelyik metódusa, felhasználói beavatkozás nélkül, 
        előre meghatározott időpontokban fusson le.
      
- 
        Az időzítés csak akkor működik, ha valamelyik @Configuration annotációval ellátott osztály meg van jelölve 
        az @EnableScheduling annotációval is.  
      
- 
        Az időzítendő bean metódust el kell látni a @Scheduled annotációval.
      
- 
        Példa: a bean metódus vége után 5 másodperc szünet következik, majd újra futtatja az időzítő a metódust:
        
      
- 
        Példa: a bean metódus kezdete után már időzíti a következő futtatást 5 másodperc múlva.
 Ebben az esetben az időzítő számára lényegtelen mennyi ideig fut a metódus:
- 
        Példa: unix szabványos cron időzítés, minden másodpercben fut:
        
      
- 
        Példa: unix szabványos cron időzítés, minden munkanap fut hétfőtől péntekig, minden nap egyszer, 22:30-kor:
        
      
- 
        A cron időzítés részletesebben megtekinthető a CronSequenceGenerator osztály api dokumentációban.
      
- 
        Egy cron-kifejezésről eldönthető hogy helyes-e:
        
      
- 
        Ellenőrizhető hogy egy cron-kifejezés milyen dátumokra fog időzíteni:
                
      
@Service
    
      - 
        Ezt az annotációt olyan spring-bean-re kell alkalmazni mely általános műveleteket végez.
 Pontosabban kifejezi a spring-bean funkcióját, mintha a @Component-et alkalmaznánk rá.
@SessionAttributes
    
      - 
        Controller osztályhoz tartozó annotáció.
 Model attribútumok és session attribútumok közötti szinkronizációs hatást vált ki a @SessionAttributes 
        annotációban megadott attribútumokkal.
 A szinkronizációs hatást a controller metódusaira fejti ki.
- 
        Működés:
        
        
          - 
            Controller metódus futása előtt:
 A @SessionAttributes-ban felsorolt névvel a session-ben lévő attribútumot a spring automatikusan berakja 
            a model-be is,
 ugyanezzel az attribútum névvel.
- 
            Controller metódus futása után:
 A @SessionAttributes-ban megadott névvel a model-ben lévő attribútumot a spring automatikusan 
            berakja a session-be is,
 ugyanezzel az attribútum névvel.
- 
            Form post-ot fogadó controller metódus futása előtt:
 Ha a metódusban a @ModelAttribute és @SessionAttribute nevek megegyeznek, akkor a spring nem hoz létre 
            új bean példányt, hanem a session-ben lévőt használja, ebbe kerülnek a post adatok. Jól lehet használni 
            ha a bean-be beállítunk egy olyan értéket amit a form nem adna vissza, és az értéket meg akarjuk őrizni. 
            Vagy pedig egy lekérdezés szűrőfeltételeit szeretnénk megőrizni, 
            hogy legközelebb erre a lapra navigálva a szűrőfeltételek megmaradjanak.
 
- 
        Példák:
        
      
- 
        Amikor már nincs szükség a @SessionAttributes által megnevezett, a session-be tárolt attribútumokra, 
        akkor ezt kell hívni:
        
        Kitörli a session-ből azokat az attribútumokat amiket felsoroltunk a @SessionAttributes annotációban. 
        Nem probléma ha a setComplete() nincs meghívva, ugyanis ha több controller ugyanazt az attribútum nevet használja, 
        akkor a session-ben ezek egymást felülírják, tehát ugyanezzel a névvel csak egy tud tárolódni.
      
@SessionScope
    
      - 
        Session scope bean létrehozása, mely ezzel egyenértékű:
        
      
- 
        Ezeket a bean-eket meg lehet semmisíteni a HttpSession.invalidate() metódussal, 
        és a következő http request-nél a spring újakat fog belőlük létrehozni.
        Vagy pedig bekövetkezik a session timeout, és a következő http request-nél a spring szintén újakat 
        fog belőlük létrehozni.
      
@SpringBootApplication
    
      - 
        Spring boot alkalmazást indító osztály annotációja, mely 3 annotációt helyettesít:
        
      
- 
        Mivel a @ComponentScan annotációt is magába foglalja, meg lehet adni azokat a java csomagokat melyekben a spring 
        a komponenseket keresni fogja. 
      
- 
        Ha a @SpringBootApplication-t a scanBasePackages beállítása nélkül alkalmazzuk, akkor ezt az osztályt 
        tartalmazó csomagban, illetve alcsomagjaiban fogja a spring keresni a komponenseket:
        
      
- 
        Ha a @SpringBootApplication-t a scanBasePackages beállítással alkalmazzuk, akkor pont ezekben a csomagokban, 
        és alcsomagjaikban fogja keresni a spring a komponenseket, a @SpringBootApplication-t tartalmazó package-ben nem:
        
      
@Value
    
      - 
        A @Value annotáció spring bean-eken belül működik. 
        Az aktuális környezeti beállítások kérdezhetők le vele, és ezt egy bean tagjának lehet értékül adni.
      
- 
        Az aktuális környezeti beállításokat a spring csak egyszer állítja össze, az alkalmazás indulásakor!
 Tehát program futás közben hiába módosítanánk egy külső application.yml fájlban, 
        ez a változás nem jut érvényre még akkor sem ha request-scope bean-t alkalmazunk @Value annotációval, 
        vagy az Environment objektumot olvassuk be.
- 
        A @Value miatt a spring a környezeti beállítás property-t megpróbálja konvertálni a @Value változójába, 
        a változó típusának megfelelően.
        Ha ez nem sikerül, az alkalmazás el sem indul, és a hibaszöveg tartalmazza a konverziós hibát.
      
- 
        Lásd még: "application.yml" leírás.
      
- 
        Példa, az aktuális szerver port lekérdezésére, mely alapján egy változót állítunk be:
        
      
- 
        Példa 2:
        
      
- 
        Default értékek megadása
 Az alkalmazás el sem indul, exception keletkezik, ha nem létezik az a környezeti beállítás amire
        a @Value hivatkozik.
 Erre találták ki a default értéket, melyet a környezeti beállítás hiányában fel tud venni a változó.
 A defult érték megadása a ":" karakterrel lehetséges:
        
        Tehát a null default értékhez, spEL-t kell alkalmazni.
- 
        Dokumentáció
      
active mq
    
    
    actuator
    
      - 
        Az actuator segítségével monitorozni lehet az alkalmazást, megtekinthető a memória használat,
        a bean-ek, session-ök ...stb.
      
- 
        Az actuator aktiválása után bizonyos path-ok (endpoint-ok) hívhatók lesznek,
 melyek text (json) kimenetként szolgáltatják az adatokat az alkalmazás állapotáról.
- 
        Az actuator aktiválása:
 1. lépés: pom.xml kiegészítése:
        
        2. lépés: application.yml kiegészítése, hogy az összes actuator endpoint-hoz hozzá lehessen férni:
- 
        Endpoint-ok:
 Ezek a path-ok akkor is működnek, ha van egy saját interceptorunk, mely nem engedné őket meghívni.
        Valszeg az actuator úgy oldja ezt meg, hogy egy filtert csinál, ami előbb fut le mint a saját interceptorunk,
        és ez szolgálja ki az alábbi path-okat:
- 
        dokumentáció
      
ApplicationContext
    
      - 
        Spring bean elérését teszi lehetővé dinamikusan, például:
        
      
- 
        Hogy az ApplicationContext-hez bármelyik java osztályban hozzá lehessen férni, a MyApplication.java osztályban 
        készítsünk egy @PostConstruct metódust. Ebben a metódusban hozzáférünk az ApplicationContext-hez, 
        és az értékét tegyük bele az osztály "context" névvel ellátott statikus tagjába.
        Így bármelyik osztály hozzá tud férni az ApplicationContext-hez:
        
        és használhatja ennek metódusait, például hozzáférhet a spring bean-ekhez:
        
      
ApplicationRunner
    
      - 
        Ha az alkalmazás indításával egyidőben szeretnénk programkódot futtatni, akkor az ApplicationRunner interface-t 
        implementáló osztály lehet rá megoldás.
 Figyelem: amíg az ApplicationRunner osztály run() metódusa fut, addig is meg lehet hívni az 
          alkalmazást http kéréssel!
 Ha ezt nem akarjuk, akkor az ApplicationRunner helyett @EventListener-t kell használni!
- 
        Példa:
        
      
- 
      
application.yml
    
      - 
        Dokumentáció: 
        Externalized Configuration
      
- 
        Alkalmazás beállítások tárolására szolgál. Default helye a projektben:
        
        vagy érdemes a config alkönyvtárba tenni ha a profile-ok miatt több van belőle:
        
      
- 
        Az application.yml fontosabb beállításai:
        
          - 
            Ezen a porton fut az alkalmazás. Default érték: 8080
            
          
- 
            Context path. Default érték: nincs context path
            
            Állhat több tagból is:
            
          
- 
            Aktív profile-ok, vesszővel elválasztva
            
          
- 
            Session timeout.
 Ha csak számértéket adunk meg, akkor másodpercben értendő.
            A számérték után megadhatunk duration értéket is.
            
            A beállítástól függetlenül minimum 1 percig él a session, és percekre kerekít,
            lásd: HttpSession.getMaxInactiveInterval()
 
- 
        A környezeti beállítások megadhatók külső és belső application.yml fájlokban, program indítás argumentumként,
        operációs rendszer környezeti változókban stb. 
        Meg van határozva melyiknek mekkora a prioritása, lásd a spring boot dokumentáció 
        "24. Externalized Configuration" fejezetét.
      
- 
        Bármilyen bejegyzés ami be van állítva az application.yml fájlban, program indítás argumenként is megadható,
        ha ugyanezt a kulcsot használjuk "--" jellel kiegészítve, tehát a parancssor argumentum az erősebb. 
        De az operációs rendszerben beállított környezeti változók is erősebbek mint a belső 
        application.yml bejegyzések.
      
- 
        Nemcsak a spring-es, hanem saját környezeti beállítások is használhatók, például: "my.proba" 
      
- 
        Példa: tegyük fel hogy a belső application.yml tartalmazza hogy a program melyik porton fusson: 
        
        Ezesetben a program a 48001-es porton indul el:
        
        Ezesetben a program a 48002-es porton indul el, mivel a macOS környezeti változója erősebb mint a 
        belső application.yml beállítás:
        
        Ezesetben a program a 48003-as porton indul el, mivel a program indítás argumentumként megadott érték erősebb 
        mint a macOS környezeti változója, és a belső application.yml beállítás:
        
      
- 
        Példa: programon belül tároljuk a különböző környezetekhez tartozó beállításokat, 
        profile függő application-{profile}.yml fájlokban,
 és a program indítási parancssorban állítjuk be a spring.profiles.active értékét ("teszt" vagy "eles").
 
 application.yml:
        
        application-teszt.yml:
        
        application-eles.yml:
- 
        Külső (nem a jar-ba csomagolt) application.yml:
        
          - 
            A working directory-ba, vagy az alatta lévő "/config" könyvtárba kell tenni a yaml-fájlt, 
            nem pedig a jar-t tartalmazó könyvtárba.
          
- 
            Ha program futás közben módosítjuk a yaml-fájlt, a spring ezt figyelmen kívül hagyja.
 Spring Cloud környezetben + a @RefreshBean annotáció használatával lehet ezen a működésen változtatni.
 
application restart
    
      - 
        Lehetőség van arra, hogy a program önmagát újraindítsa, mely akkor lehet hasznos ha változtak a külső 
        property (yaml) fájlok, és azok új értékeivel kell a programnak működnie. Ehhez módosítani kell az alkalmazást
        elindító java osztályt úgy, hogy létrehozzuk a statikus restart() metódust:
        
        Ezután nincs más teendő mint pl. egy tetszőleges URL meghívására (/restart) a controller 
        elindítsa ezt a fenti metódust:
        
      
- 
        dokumentáció
      
bean validation
    
      - 
        Lásd még: java info ➔ bean validation
      
- 
        Spring esetén a bean validáció automatikusan történik meg a standard java-val ellentétben, mivel a validáció 
        aktiválását, és a hibakezelést a spring elvégzi helyettünk. Ehhez az automatizmushoz szükséges a pom.xml 
        kiegészítése a spring-boot-starter-validation dependency-vel.
      
- 
        A spring létrehozza a ValidatorFactory és Validator osztályokat, így ha mégis manuális akarjuk 
        ezeket használni, akkor csak be kell húzni az osztályba: 
        
      
- 
        @Valid
 Rest service esetén a json-t fogadó java objektumban szükségesek a validációs annotációk.
 Ezenkívül a controller metódusban a json-t fogadó java objektum elé a @Valid annotáció is szükséges, 
        hogy a spring aktiválja a validációt.
- 
        @Validated
 Ezzel az annotációval request paramétert lehet validálni úgy, hogy a controller osztályra 
        kell helyezni a @Validated annotációt, és a controller metódusban a paraméter elé kell elhelyezni 
        a validációs annotációt:
BindingResult
    
      - 
        A BindingResult objektummal végezhető fontosabb műveletek:
 Lekérdezhető (de nem írható át) az eredeti érték amit a felhasználó a felületen bevitt: 
        
        Field error beállítása:
        
        Global error beállítása:
BuildProperties
    
      - 
        Ennek az osztálynak a használatával hozzáférhetünk többek között a pom.xml-ben lévő program verzióhoz,
        és a program build időpontjához is ahelyett,
 hogy a maven replacer plugint használnánk.
- 
        A pom.xml-ben lévő spring-boot-maven-plugin-t ki kell egészíteni egy új execution résszel:
        
      
- 
        A pom.xml kiegészítése, és egy program build után már használható a BuildProperties osztály
        egy tetszőleges spring bean-ben: 
        
      
- 
        Használat thymeleaf-ben:
        
      
- 
        Működés: program build esetén a spring-boot-maven-plugin elkészít egy fájlt, melybe belerakja a build-információkat, 
        és a spring ebből a fájlból tudja kiolvasni:  
        
      
cache
    
      - 
        Cache-Control
 Ezzel a http response header-rel lehet vezérelni, hogy a böngésző használjon-e cache-t.
 Cache letiltása esetén elég ha ezt tartalmazza a http-header: 
        
        Ha viszont az a cél, hogy az adott fájlt a böngésző cache-ben tárolja egy ideig, akkor egy jó beállítás:
        
        A fenti példában a fájl maximum 1024 másodpercet (bő 17 percet) tölthet a publikus cache-ben.
 Ha a fájl kritikus információt tartalmaz, melyet nem láthat más felhasználó, 
        akkor a private cache használatát kell a header-ben beállítani.
- 
        Cache beállítás weblapok számára
 Weblapok esetén nem ajánlott a cache használata, a böngészőnek mindig a szerverhez kell fordulnia, hogy
        friss információval rendelkező lapok jelenjenek meg. Ennek eléréséhez készíthetünk saját interceptort mely
        beállítja a http-header-t, de van erre kész megoldás is, a WebContentInterceptor osztály,
        amit be kell állítani, és regisztrálni:
- 
        Cache beállítás static resource-ok számára
 A statikus resource-ok nem változnak a program futása során, ezért ezeket mindenképp érdemes a cache-ben tárolni.
 Tipikus eset amikor az alkalmazás minden lapja ugyanazt a háttérképet tartalmazza.
 Ha a háttérkép nem tárolódik cache-ben, így mindig a szerverről töltődik le, villogás tapasztalható.
 A statikus resource-okra vonatkozó cache beállítható a WebMvcConfigurer-t implementáló osztályban:
        
        A fenti példa ezt a http response header-t eredményezi:
        
        Egy másik lehetőség, hogy az application.yml fájlban állítjuk be ezeket, például a max-age értékét:
- 
        Firefox F12
 Ebben az ablakban a Hálózat fülön az Átküldve oszlopban is látható, 
        hogy a böngésző a szerverről töltötte le, vagy a cache-ből vette elő a fájlt.
 A szerverről letöltött fájl esetén látható a fájl mérete, 
        cache esetén viszont a gyorsítótárban szöveg jelenik meg.
conversion
    
      Http-get paraméter → java változó
      Html form ↔ java változó
    
    
      - 
        Konverziót megvalósító osztályok:
        
          - Converter (Spring 3.0-tól)
- Formatter (Spring 3.0-tól)
- PropertyEditor (JavaSE része)
 
- 
        Egy konkrét mezőre is beállítható ?
        
          - Converter: nem állítható be
- Formatter: Controller @InitBinder metódusban: WebDataBinder.addCustomFormatter(...)
- PropertyEditor: Controller @InitBinder metódusban: WebDataBinder.registerCustomEditor(...)
 
- 
        Konvertálható adattípusok ?
        
          - Converter: bármilyen típus - bármilyen típus
- Formatter: bármilyen típus - String
- PropertyEditor: bármilyen típus - String
 
- 
        A kétirányú konverzió leírásához hány osztály kell ?
        
          - Converter: 2
- Formatter: 1
- PropertyEditor: 1
 
- 
        Beépített locale függő konvertálást is tud ?
        
          - Converter: nem tud
- Formatter: igen tudja, a konverziós metódusok kapnak Locale paramétert
- PropertyEditor: nem tud
 
- 
        Üres form-mezőre is fut ?  
        
          - Converter: igen
- Formatter: nem
- PropertyEditor: igen
 
- 
        Fut-e a konverzió ha egy url-paramétert (@RequestParam, @PathVariable) kell konvertálni változóba ?
        
          - Converter: igen
- Formatter: igen
- PropertyEditor: igen
 
- 
        Converter:
        
          - 
            Saját konvertert a Converter interface implementálásával lehet készíteni. 
          
- 
            Ha olyan osztályra készítünk konvertert, melynél a spring-nek nincs beépített konvertere 
            (pl. String -> java.awt.Color konverter),
            akkor az általunk készített konverter működik. 
          
- 
            Ha olyan osztályra készítünk konvertert, melynél a spring már rendelkezik beépített konverterrel 
            (pl. String -> BigDecimal konverter) akkor a működés a következő:
            Lefut az általunk készített konverter. Ha sikeresen átkonvertálta az értéket, akkor a művelet befejeződött.
            Ha nem konvertálta át sikeresen, és bármilyen exception-t dob, akkor a spring még lefuttatja a 
            beépített konverterét (hátha annak sikerül). 
            Ha a beépített sikeresen lefut, akkor sikeres a konverzió és nincs hiba, 
            ha nem akkor valóban typemismatch hiba keletkezik.
          
- 
            A spring beépített konverterét nem tudtam kikapcsolni.
          
 
- 
        Formatter:
        
          - 
            print() működése:
            Ha egy típushoz rendelt formatter print() metódusa hibát dob, akkor a spring nem próbálja meg az ugyanilyen 
            típushoz rendelt beépített formatterét meghívni, így a hiba megmarad. 
            Ha egy mezőhöz van hozzárendelt formatter, akkor sem hívja meg a saját globális formatterünket, 
            hátha annak sikerülne a print().
          
- 
            parse() működése:
            Ha egy típushoz rendelt formatter parse() metódusa hibát dob, akkor a spring meghívja az ugyanilyen 
            típushoz rendelt beépített formattert,
            hátha annak sikerül a parse() végrehajtása, és így lehet hogy mégsem keletkezik hiba.
            Ha egy mezőhöz rendelt formatter parse() metódusa dob hibát, akkor a spring nem próbálja meghívni az 
            ugyanilyen típushoz rendelt beépített formattert.
          
 
- 
        Összegzés:
        
          - 
            Saját konverziós eljárás esetén a legjobb ha Formatter-eket készítünk, és ezeket beállítjuk globálisan. 
            Ha egy-egy mezőhöz más kell, akkor azokhoz más formattert állítsunk be. 
          
- 
            String típusú változó esetén ne "string to string" formattert készítsünk, hanem erre a célra 
            PropertyEditor-t használjunk. 
          
 
Yaml → java változó
    
      - 
        Az application.yaml-ból való beolvasás esetén a default konverzióra érdemes hagyatkozni, ne módosítsunk rajta!
      
- 
        Egyes leírások megemlítik, hogy a Converter osztályt + @ConfigurationPropertiesBinding 
        annotációt lehetne használni magunk által megírt konverzióra, de a mintapéldák sem működnek.
      
- 
        A Converter a dokumentációja alapján a null értékre nem is fut, pedig a yaml-ban beállítható null is.
 Ha a yaml null értéket tartalmaz a fogadó String típusú változóba üres string kerül, nem pedig null.
- 
        Ha a yaml-ban null van, annak mi a típusa, milyen típusra kellene converter-t írni?
      
Json ↔ java változó
    
      - 
        Erre nem hatnak a PropertyEditor, Converter beállítások, mivel a Jackson saját maga kezeli a konverziót. 
      
duration
    
      - 
        Az idő, időtartam jellegű property-k esetén a számérték után megadható egy mértékegység (duration) is.
 Ezek többfélék lehetnek, ezek közül néhány fontosabb:
- 
        Példa: session timeout beállítása 3 órára:
        
      
Environment
    
      - 
        A program indításakor aktuális környezeti beállítások lekérdezése.
 Ha a program futása közben módosítunk egy külső application.yml fájlban, 
        az már nem módosítja az Environment-et!
- 
        Dokumentáció: 
        Externalized Configuration
      
- 
        Lásd még: "application.yml" leírás, @Value
      
- 
        Példa:
        
      
- 
        Példa:
        
        
          - null, ha sehol sincs definiálva
- "xy", ha csak az application.yml-ben van definiálva hogy: "my.proba: xy"
- "xy", ha csak a program indításnál van definiálva hogy: "--my.proba=xy"
- 
            "xy", ha az application.yml-ben "my.proba: tz" van, a program indításnál pedig: "--my.proba=xy", 
            tehát a program indítási beállítás az "erősebb"
          
 
exception handler
    
      - 
        Exception keletkezéskor a spring azt a lehető legkisebb szintű exception handlert hívja meg, 
        mely alkalmas az exception kezelésére.
      
- 
        1. szintű exception handler:
 Egy controlleren belül keletkező exception kezelésére szolgál, így az @ExceptionHandler annotációval 
        ellátott metódust a controlleren belül kell elhelyezni. Egy controlleren belül több @ExceptionHandler lehet, 
        melyek más-más exception-t kezelnek le. A metódusban használhatjuk az Exception objektumot paraméterként, 
        így a spring átadja a metódusnak a keletkező exception-t.
        Például 2 exception-t egyszerre lekezelő handler:
- 
        2. szintű exception handler:
 Az összes controlleren belül keletkező exception kezelésére szolgál. Felépítése megegyezik az 1. szintű handler-rel, 
        de a metódust nem controller-ben kell elhelyezni, hanem a @ControllerAdvice annotációval ellátott osztályban.
 
 Interceptorban keletkező exception-t nem minden esetben lehet vele elkapni. Ha interceptorban keletkezik exception, 
        de a hívott url nem létezik, akkor mindenképp a 3-as szintű exception handler fog lefutni a 2-es helyett.
- 
        3/A. szintű exception handler:
 Általunk készített osztály, mely implementálja az ErrorController-t, és az "/error" url-t kezeli le.
        Ebben az osztályban csak egy controller metódus lehet, ami az "/error" url-t kezeli le, így abban kell elágaztatni,
        hogy különböző hibák esetén mi történjen. Ezen a szinten minden exception elkapható, olyanok is melyek nem 
        kaphatók el 1. és 2. szintű @ExceptionHandler-rel, mert nem controlleren belül keletkezik a hiba, például:
          - Thymeleaf view értelmezési hiba.
- Semmilyen controllerhez sem tartozó URL meghívása.
 Ha ehhez az exception handler-hez még külön információt is el akarunk juttatni, akkor egy spring-bean -be 
        berakva az információt, ez a handler is ki tudja olvasni, és ez működik a @RequestScope bean-ekkel is. Példa:
- 
        3/B. szintű exception handler:
 A spring beépített exception handlere, mely egy "Whitelabel Error Page" feliratú weblapot jelenít meg.
        Ez egy olyan oldal, mely csak nagyon felületes információt szolgáltat a keletkezett exception-ről.
        Ha készítünk 3/A szintű exception handler-t, az elfedi a 3/B szintű exception handler-t, így a 3/B sosem jelenik meg.
- 
        4. szintű exception handler:
 A spring beépített, nem kikapcsolható exception handlere.
 Ha a hiba eljut erre a szintre, akkor a spring egy http 500-as hibaoldalt küld, 
        mely a stack trace-t is tartalmazhatja.
 A hiba a log-állományból kideríthető.
- 
        Ha az 1-es, vagy 2-es szintű exception handlerben exception keletkezik, 
        akkor a 3-as szintű exception handlert hívja meg a spring.
      
- 
        Ha az 3-as szintű exception handlerben exception keletkezik, akkor a 4-es szintű exception handler fut le.
      
file download
    
      - 
        Fájl letöltés a ResponseEntity osztály segítségével:
        
      
- 
        Fájl letöltés direkt HttpServletResponse írással:
        
      
file upload
    Általános eset: normál html (multipart)
    
      - 
        Html-ben a <form> elemnél beállítandó attribútum:
        
      
- 
        Html-ben a fájl feltöltés mezője:
        
      
- 
        Form bean adattípus mely fogadja a feltöltött fájlt (fájlokat):
        
      
- 
        A MultipartFile objektum segítségével a fájl tartalmát bájtsorozatként, és stream-ként is megkaphatjuk:
        
      
- 
        Így kell vizsgálni hogy nincs feltöltött fájl, ugyanis microsoft edge böngészőnél a multipartFile 
        objektum lehet null is:
        
      
- 
        Maximális fájl méret beállítása az application.yml fájlban, 
        és érdemes beállítani a http request maximális méretét is:
        
      
- 
        Fájl feltöltés könyvtárának beállítása az application.yml fájlban.
 Így a feltöltött fájlok közvetlenül a "/tmp" könyvtárba kerülnek, nem pedig annak valamelyik alkönyvtárába:
        
        Ez a beállítás csak azért szükséges, hogy egy hibát elkerüljünk:
 Program induláskor a tomcat alkönyvtárakat hoz létre  a "/tmp" alatt, melyet fájl feltöltéshez is használ. 
        Azonban az operációs rendszer a "/tmp"
        könyvtár tartalmát kiürítheti, ha pl. 10 napig nem módosult a könyvtár tartalma. 
        Ezután a fájl feltöltés nem fog működni, a következő hiba keletkezik:
 Failed to parse multipart servlet request; nested exception is java.io.IOException:
 The temporary upload location [/tmp/tomcat.8789959165287795690.50001/work/Tomcat/localhost/context-path] 
          is not valid
- 
        Ha tomcat-et használunk az alkalmazás futtatásához, és túl nagy méretű fájlt (>20MB) töltünk fel, akkor a tomcat 
        
          connection reset
        -et csinál,
        vagyis megszakítja a http kapcsolatot, így a felhasználó nem spring-es hibaüzenetet kap, 
        hanem a böngésző üzen neki vissza, hogy az oldal nem elérhető.
        Ha ezt nem szeretnénk akkor át kell állítani a max swallow size értéket az application.yml fájlban:
        
      
- 
        A feltöltés folyamata:
 A feltöltés úgy történik, hogy a kliens elkezdi küldeni a fájlt. Ha a köldött adatmennyiség eléri a 
        max swallow size értékét, akkor connection reset történik.
 Ha nem történik connection reset a feltöltés végéig (mert a max swallow végtelenre van állítva, 
        vagy nagyobbra mint a feltöltött adatmennyiség), akkor a spring kapja meg a vezérlést, és ellenőrzi hogy az 
        application.yml-ben beállított értéket meghaladta-e az adatmennyiség.
        Ha meghaladja akkor MaxUploadSizeExceededException keletkezik, mely a MultipartException leszármazottja.
        A MaxUploadSizeExceededException-t nem lehet lekezelni a form post-ot fogadó controllerben, 
        vagyis nem működik az 1-es szintű exception handler, 
        de le lehet kezelni 2-es, vagy 3-as szintű exception handlerben.
- 
        Egy jó megoldás:
 A "max swallow size" méretet be kell állítani akkorára, amekkora adatmennyiséget megengedünk (pl. 5 mbyte).
        Így egy óriási fájl feltöltésekor pár másodpercen belül bekövetkezne a connection reset, a feltöltés nem tart 
        percekig, és nem terheli a hálózatot. Hogy a felhasználó rendes hibaüzenetet kapjon, egy javascript-nek kell 
        ellenőriznie mekkora a fájl mérete, és a javascript-nek nem szabad engednie a fájl feltöltését, 
        ha az túllépi a megengedett méretet. 
        A javascript megoldás további előnye, hogy különböző weblapokon különböző fájl méreteket lehet beállítani.
Speciális eset: frontend framework, melynek a kérését json-ben fogadjuk
    
      - 
        A fájl base64 kódolással szövegre alakítható, így egy json belsejében küldhető a szerver felé, 
        pont úgy mintha egy szöveg lenne. 
      
- 
        A probléma az, hogy a Jackson alapból csak maximum 20 megabájtos json-t hajlandó beolvasni.
 Ezt a beállítást a StreamReadConstraints osztály tartalmazza, tehát ezt kéne módosítani.
- 
        A megoldás a 20 megabájtos korlát felemelésére: definiálunk egy saját StreamReadConstraints osztályt:
        
      
- 
        Sajnos globálisan nem lehet beállítani a MyStreamReadConstraints használatát, ez nem hatásos, 
        hiába fut le a program indulásakor:
        
      
- 
        A globális beállítás helyett, ahol óriási méretű json-t fogadnánk, ott a request body-t String-be kell beolvasni, 
        és a Jackson konverziót a mi kódunknak kell elvégezni a spring helyett, így a Jackson objectmapper-t be tudjuk 
        úgy állítani, hogy tudja fogadni az óriási méretű json-t:
        
      
Speciális eset: frontend framework, melynek a kérését stream-ben fogadjuk
    
      - 
        Ebben az esetben egy frontend framework segítségével történik a fájl feltöltés, a feltöltési folyamat kliens
        oldala tetszőlegesen programozható.
      
- 
        Nem túl nagy fájlok esetén a fájl base64 kódolással szövegre alakítható, így egy json belsejében küldhető 
        a szerver felé, pont úgy mintha egy szöveg lenne. 
      
- 
        Túl nagy fájlok esetén a json-be csomagolás nem célravezető, hiszen azt egy java objektumban fogadja a spring,
        így az egész a memóriában jön létre egyszerre, és lehet hogy ott nem fér el. Ezért jobb lenne helyette stream-et 
        alkalmazni.
 Egy lehetséges megoldás:
 A szerver hívást bontsuk 2 részre:
          -         
            A frontend az 1. hívásban json-ben küldi a kisebb adatokat, melyet a szerver egy session bean-ben tárol, 
            és válaszként visszaadja, hogy a hozzátartozó nagy fájlt milyen azonosítóval kell felküldeni a 2. hívásban.
          
- 
            A frontend a 2. hívásban csak a fájl tartalmát küldi a request body-ban bájtsorozatként, és a http-header-ben 
            beállítja az 1. hívásban kapott fájl azonosítót. A 2. hívást a spring így tudja fogadni stream-ként:
        
        
 
Http-request méret maximalizálása bármely esetre
    
      - 
        Nem minden fájl feltöltés multipart, a speciális esetekben nincs korlát a request méretére, hiszen
        fogadhatjuk a request-body-t bájttömb-ben, Stringben is.
 Ha korlátozni szeretnénk a http-request méretét, akkor egy interceptorban kell ellenőrizni, 
        és a request-et elutasítani, hogy ne jusson el a controller-ig.
form
    
      - 
        Az űrlapokhoz tartozó form objektum tagjai private-ok legyenek, 
        és szükséges a hozzájuk tartozó getter + setter metódusok.
        
 Ha a form objektum tagjai public-ok, és nem tartozik hozzájuk getter + setter, akkor sajnos a thymeleaf
        nem tudja a form tagjait írni, és olvasni, exception keletkezik.
Formatter
    
      - 
        String és más adattípus közötti konverzió, melynek hatása ezekre az esetekre vonatkozik: 
        
          - Form mező és form bean közötti konverzió mindkét irányban.
- URL-ben lévő request paraméter konvertálása @RequestParam változóba.
 
- 
        Hasonló a converterhez, de annál azért jobb mert a locale beállítást is megkapja paraméterként, 
        tehát locale függő konverziót lehet benne megvalósítani.
      
- 
        A formatter beregisztrálása az alkalmazásba az @InitBinder annotációval történik.
      
- 
        Formatter példa java.util.Date típus esetén:
 A dátum a képernyőn "éééé.hh.nn" formában jelenjen meg, továbbá az "éééé.hh.nn" stringként bevitt adat a 
        Date típusú model-be bekerülhessen:
interceptor
    
      - 
        Interceptor használatával be lehet avatkozni egy url hívás folyamatába, 
        meg lehet akadályozni hogy egy controller-hez kerüljön a vezérlés, módosítani lehet a http headert.
      
- 
        Az interceptor egy olyan @Service annotációval jelölt bean, melynek metódusai alapesetben minden 
        http request-re lefutnak.
      
- 
        Az interceptornak implementálnia kell a HandlerInterceptor interface-t. 
      
- 
        Több funkció esetén javasolt több interceptor használata, egyikben pl. a login ellenőrzése, 
        a másikban a http header beállítása történhet meg.
      
- 
        Az interceptorok lefutási sorrendje megegyezik a regisztrációs sorrenddel, 
        azonban biztosabb ha az order() metódussal állítjuk be.
      
- 
        Alapesetben az interceptor minden url-hívásra fut, ha csak szimplán regisztráljuk. 
        Lefut ha az url-hez tartozik controller, ha nem tartozik, ha az url static resource-ra hivatkozik...
        Ez a működés módosítható az interceptor regisztrációjánál, ezekkel a metódusokkal:
        
          - 
            excludePathPatterns()
 Az itt megadott url-ekre nem fog futni.
- 
            addPathPatterns()
 Ha ezt a metódust használjuk, akkor kizárólag csak az itt megadott url-ekre fog futni.
 
- 
        Ha egy controller redirect-ál egy másik controllerhez, vagyis egy url hívásakor 2 controller is fut, 
        akkor az interceptor is lefut mindkét alkalommal.
      
- 
        Végrehajtási folyamat 2 interceptor esetén:
        
      
- 
        preHandle() metódus
 Ha szükséges, megakadályozza hogy a controller-hez kerüljön a vezérlés, ez a visszatérési értékétől függ.
 Ha szükséges, ebben a metódusban érdemes a HttpServletResponse módosítását is elvégezni, pl. a header beállítást.
          - 
            True visszatérési érték esetén:
 A fenti végrehajtási folyamat folytatódik.
- 
            False visszatérési érték esetén:
 A fenti végrehajtási folyamat megszakad. A kimenet normál 200-as státuszú, 0 byte-os lesz.
 Ezért érdemes egy http redirect-et is küldeni, hogy egy másik controller-hez kerüljön a vezérlés.
- 
            Exception esetén:
 A fenti végrehajtási folyamat megszakad. 
            Az exception handler-t pont úgy keresi meg a spring, mintha a controllerben keletkezett volna.
 
- 
        postHandle() metódus
 Általában üresen hagyjuk.
 Azelőtt fut le hogy a spring a view renderelést elvégezte volna. Mivel ModelAndView objektumot kap 
        paraméterként szükség esetén módosíthatja.
        Itt is lehetséges a HttpServletResponse módosítása, azonban nem érdemes ebben a metódusban ezt elvégezni.
        Ugyanis lehet, hogy a HttpServletResponse már nem módosítható (committed=true állapotban van) azon esetekben 
        amikor a view-t már nem kell renderelni pl:
 - a controller @ResponseBody módon már előállította a kimenetet
 - controller helyet static resource-ra hivatkozik az url, mely kész van
- 
        afterCompletion() metódus
 Általában üresen hagyjuk.
 Azután fut le hogy a spring a view renderelést elvégezte. Itt már hatástalan a HttpServletResponse módosítása 
        hiszen már committed=true állapotban van.
- 
        Interceptor ne fusson a globális error handler előtt, vagyis a "/error" url-re!
 Ugyanis ha az interceptor preHandle() metódusban keletkezik a hiba, akkor sose jutna el a vezérlés a "/error"-hoz,
        hiszen az előtt is lefutna a preHandle() ami ismét hibát generálna. Így a spring beépített 4-es szintű exception 
        handlere lépne működésbe.
- 
        Példa: Ha a felhasználó nincs bejelentkezve és nem publikus lapra szeretne navigálni, akkor a felhasználót a 
        bejelentkező lapra irányítjuk:
        
        A fenti interceptor regisztrációja egy konfigurációs osztályban:	  
         
      
- 
        Példa 2 interceptor regisztrációjára, ahol a lefutási sorrendjük is be van állítva.
 A 2. interceptor nem fut a "/error" url-re, és a static resource-ra.
Jackson
    
      - 
        Spring Boot használatával automatikusan bekerül a project-be a Jackson library.
 Ez a library json konvertálást végez java objektumba, és vissza.
- 
        A Jackson a json-re konvertáláskor megtartja az osztályban lévő tagok sorrendjét.
 Szintén megtartja a LinkedHashMap osztályba berakott kulcs-érték párok sorrendjét.
 Ha egy osztály-tag, vagy map-kulcs értéke null, akkor is megjelenik a json-ben.
 
- 
        Az osztályt nem kötelező @Getter/@Setter annotációkkal ellátni, elég ha a tagok láthatósága: public
      
- 
        Osztály ➔ json konverzió
        
        Használható kész osztály is, például: Map
 
- 
        Json ➔ osztály konverzió
        
        Ha nincs saját osztályunk, használjuk a JsonNode-ot:
        
        Mindig osztályt adjunk meg, ne pedig egy generikus típust, különben nem lesz típus helyes a betöltés.
 Tehát ne egy osztály List<Szemely> típusú tagját adjuk meg a beolvasáshoz.
 Osztály esetén a Jackson a reflection api segítségével felderíti az osztály belsejében lévő generikus tagokat, 
        és az adatokat típus helyesen tölti be.
- 
        ObjectMapper beállítások
 A saját programkódunk által létrehozott ObjectMapper példány néhány beállítása:
          - 
            Nem keletkezik UnrecognizedPropertyException, ha a json több adatot tartalmaz, 
            mint amennyit az osztály fogad:
            
          
- 
            LocalDate, LocalTime típusokat tartalmazó osztály kezelése:
            
          
- 
            Formázott json előállítása, melyben sortörés és betolás is szerepel:
                    
          
 
-       
        Az osztály, és tagjai ellátható annotációkkal mely a konverziót vezérlik:
        
      
-       
        @JsonFormat
 Megadható vele a json-ben szereplő adat formátuma. Példák:
 Ha az osztály LocalDate típusú adatot tartalmaz, akkor alapesetben a json-ben "éééé-hh-nn" formában kell az 
        adatnak szerepelni.
 LocalTime esetén pedig a json-ben "óó:pp:mm", vagy "óó:pp" formában kell az adatnak lennie.
 Ha a json-ben más az adat formátum, vagy az osztályból más formátumú json-t szeretnénk előállítani, akkor az osztályban
        használni kell a fenti annotációt:
-       
        @JsonProperty
 A json kulcsok tartalmazhatnak olyan karaktereket (space, kötőjel, ...) ami miatt ugyanilyen nevű java változó nem 
        hozható létre.
 Így az osztályban eltérő nevű tagot kell létrehozni, és az annotációval jelezni, hogy a json-ben
        ez milyen névvel szerepel:
-       
        @JsonIgnore
 Az osztály ezen tagja nem lesz feltöltve a json-ből, még akkor sem ha ugyanilyen névvel tartalmaz a json adatot.
 Az osztály ➔ json konverzió során ez a tag nem kerül be a json-be.
-       
        @JsonAnySetter
 Json ➔ osztály konverzió során, lehet olyan eset amikor a json több adatot tartalmaz mint amit az osztály fogad.
 Vagy a json nem fix szerkezetű, hanem egyes kulcsai mindig változó dátumok.
 Ha ezeket az adatokat is be akarjuk olvasni, akkor egy Map típusú tagot is fel kell venni, melybe ezen kulcs-érték
        párok bekerülnek:
- 
        @JsonIgnoreProperties
 Erre az annotációra általában nincs szükség, szebb megoldás az Objectmapper beállítása, lásd fent.
 Json ➔ osztály konverzió során, ha a művelet saját programkódunk által létrehozott ObjectMapper-rel történik, 
          és az osztály kevesebb tagot tartalmaz mint a json-ben lévők, akkor UnrecognizedPropertyException keletkezik. 
          Az exception keletkezését el lehet kerülni ezzel az annotációval.        
          Az annotációt osztály szintű, és arra az osztályra kell helyezni, melyben a json-höz képest kevesebb tag van, 
          nem pedig a legfelső osztályra amit az ObjectMapper-ben leírtunk:
-       
        AC/DC szabály automatikus spring-konverzió esetén
 Ez az eset, amikor egy controller @RequestBody-val fogadja a json-t.
 Szintén ez az eset, amikor RestTemplate hívással egy osztályba fogadjuk a válasz json-t.
          - Nem probléma ha a json kevesebb adatot tartalmaz, az osztály egyes tagjai null értéket vesznek fel.
- Nem probléma ha a json több adatot tartalmaz, a json egyes adatai nem kerülne be az osztályba.
 
-       
        AC/DC szabály általunk létrehozott ObjectMapper esetén
 Ez az eset, amikor a saját programkódunk hozza létre az ObjectMapper példányt mely elvégzi a konverziót.
          - 
            UnrecognizedPropertyException keletkezik ha a json több adatot tartalmaz, mint amennyit az osztály fogad.
            
 Ezen a default működésen lehet módosítani az ObjectMapper beállítással, lásd fent.
- 
            Nem probléma ha a json kevesebb adatot tartalmaz, az osztály egyes tagjai null értéket vesznek fel.
          
 
- 
        Dokumentáció: Jackson annotációk
      
JsonNode
    
      - 
        JsonNode objektumba konvertálható egy rest service json-válasza, de a Jackson által is készíthető JsonNode objektum:
                
      
- 
        A JsonNode osztállyal csak olvasható a json-t, módosítása nem lehetséges.
      
- 
        A json struktárán belüli kulcsokat field-nek nevezi.
      
- 
        A json struktúrán belüli tömböket úgy tekinti, mintha azok is objektumok lennének, melynek kulcsai a tömb indexei.
 Ezek az indexek használhatók a get(), path() metódusokban, de vigyázat: 
        az indexeket nem string-ként, hanem int-ként kell megadni!
- 
        Json minta a lenti példákhoz:
                
      
- 
        Node-on belüli kulcsok (field-ek) lekérdezése:
                
        Tömb típusú node esetén ezzel a módszerrel nem kapjuk meg a tömb indexeit.
      
- 
        Tömb méretének lekérdezése:
                
      
- 
        Létezés lekérdezése:
                
      
- 
        Node-on belüli node elérése:
                
        Tehát a path() a get()-hez hasonló, de path()-nál nem keletkezik NullPointerException! 
                
      
- 
        Null érték lekérdezése:
                
      
- 
        Típus lekérdezése: 
                
      
- 
        Értékek elérése:
                
      
locale
    
      - 
        Két beállítás is szükséges ahhoz, hogy helyes legyen a locale értelmezése, az egyik a JVM-re hat,
        a másik a spring-re:
        
        A metódusnak kötelező "localeResolver" nevűnek lennie, különben nem lesz hatása!
 A metódusnak egy LocaleResolver implementációt kell visszaadnia, például:
          - FixedLocaleResolver
- SessionLocaleResolver.
 Az így beállított locale kihat a thymeleaf-re is, például a #dates.dayOfWeekName() más neveket ad vissza.
logging
    
      - 
        Az application.yml fájlban a logging.file.name értékét érdemes beállítani, 
        ez definiálja a log-könyvtárat és a log-fájlnevet is:
        
        Ha ez be van állítva, akkor a logging.file.path nem jut érvényre, tehát vagy az egyiket vagy a másikat érdemes
        beállítani.
      
- 
        A System.out.print()-tel való kiírás csak a konzolra megy, a log-ba nem!	
      
- 
        A beállított log-fájlba ír a spring.
 Ha programból szeretnénk ugyanebbe a log-fájlba írni, akkor a java.util.logging csomag segítségével 
        be kell állítani egy loggert:
        
        majd ezt felhasználva beleírni:
        
        de a log level-t hordozhatja a metódusnév is:
        
        ahol a severe a legmagasabb szintű logolást jelzi, mely a log-fájlban ERROR-ként jelenik meg.
- 
        Dokumentáció: a spring boot refdoc dokumentációban rákeresni a "logging.file.name" részre.
      
messages.properties
    
      - 
        Üzenetek tárolására szolgál, helye a projektben:
        
        Beállítható hogy más helyen legyen, de ez nem ajánlott!
 Azért nem, mert program indításkor azonnal keletkezhet hiba, de a messages.properties helyét beállító bean még 
        nem jött létre.
 A spring keresi a fájlt, de nem találja, és a valódi hiba helyett a messages.properties hiányára 
        utaló hiba jelenik meg a log-ban.
 Például az src/main/resources/config könyvtárban lévő messages.properties helyének beállítása,
        mely csak akkor működik, ha a metódus neve messageSource:
- 
        Lehet locale függő is, például ilyen fájlnévvel:
        
      
- 
        A messages.properties-ben tárolt szövegekben paraméterek is lehetnek: {0}, {1}, {2}, ...
 Például egy messages.properties bejegyzés:
        
        A message-re hivatkozhat például egy BindingResult, és a message paramétereket is kitöltheti:
- 
        Ha kevesebb paramétert adunk át, akkor a hibaszövegben a "{n}" megmarad, nem cserélődik ki.
      
- 
        Ha több paramétert adunk át, a feleslegesek figyelmen kívül maradnak.
      
- 
        Egy bean-ben kiolvasható a message szövege:
        
      
- 
        Typemismatch hibaüzenetek:
 Form binding esetén a spring végrehajtja az automatikus adatkonverziót, hogy a form bean megfelelő tagjaiba 
        az űrlap értékek bekerülhessenek.
        Amennyiben az adatkonverzió sikertelen, a mezőhöz a spring hibaüzenetet rendel.
 
 Példa: Ha a konverzió Long adattípusba történik, a spring ezeket a messages.properties bejegyzéseket 
        keresi ebben a sorrendben:
 Példa: Ha a konverzió long adattípusba történik, a spring ezeket a messages.properties bejegyzéseket 
        keresi ebben a sorrendben:
 Ha a fenti messages.properties bejegyzések egyike sem létezik, a spring akkor is hozzárendel hibaüzenetet 
        a mezőhöz, például:
 A hibaüzenet tartalmazhatja a {0} paramétert, de abba a spring csak a mező nevét rakja, ami nem hasznos 
        információ a felhasználóknak, így használata nem javasolt.
mixed response
    
      - 
        Lehetséges, hogy egy controller metódusnak tetszőleges response-t kell készítenie, a request paraméterétől függően.
        Ekkor így néz ki a controller metódus: 
        
      
- 
        Adott response-t milyen objektum visszaadásával lehet elérni: 
                
      
ModelAndView
    
      - 
        A ModelAndView objektumot controller metódus visszatérési értékeként használhatjuk.
 Akkor hasznos, ha a controller metódus már el van látva @ResponseBody annotációval, de mégis szeretnénk,
 hogy a spring a visszaadott stringet ne http body-nak, hanem egy megjelenítendő view-nak értelmezze:
- 
        Ha a ModelAndView konstruktorban nem adunk meg model-t, akkor is érvényesülnek a rajta kívül megadott 
        model beállítások:
        
      
- 
        Továbbá meg lehet adni a http státuszt is:
        
      
- 
        Http redirect válasz is készíthető, külső és belső címre is, továbbá használható a RedirectAttributes osztály is:
        
      
port foglaltság
    
      - 
        JAR-ként futtatott spring boot alkalmazásnál egy szabad port-ra van szükség.
        A foglalt portokat a következő paranccsal lehet kilistázni:
 Linux esetén:
        
        Macos esetén:
        
        Windows esetén:
program arguments
    
      - 
        A program argumentumokhoz hozzá lehet férni bármelyik bean-ben:
        
        Az így injektált objektum adott metódusával hozzá lehet férni az argumentumok tömbjéhez:
        
      
prototype scope
    
      - 
        Akkor használunk prototype scope-ot, amikor magunk szeretnénk vezérelni, 
        hogy egy spring-bean-ből mikor készüljön új példány.
 Egy metóduson belül akár 3 példányt is kérhetünk.
 Ezt a működést nem lehetne elérni a new operátorral, hiszen nem a spring konténerben keletkezne a bean!
- 
        Fontos, hogy a prototype bean-eket sose húzzuk be @Autowired-del, mert így elég nehéz pontosan értelmezni 
        mikor keletkezik új példány belőle. A legjobb, ha a példányosításukat programból vezéreljük úgy, hogy elkérünk egy
        új példányt az application context-ből, mely a prototype bean esetén mindig új példányt ad:
        
      
- 
        A prototype bean scope annotációja:
        
      
- 
        A spring konténer nem kezeli a prototype bean-t a teljes életciklusa alatt.
        Az ApplicationContext.getBean() metódusa elkészíti a prototype bean-t, odaadja az őt hívónak, 
        de a spring konténerben nem marad benne, nincs rá hivatkozás. Csak a hívón múlik meddig tartja meg a prototype bean-t.
        Ha nincs rá hivatkozás, a garbage collector fogja törölni a memóriából.
      
rabbitMQ
    1. módszer
    
      - 
        Az 1. módszer jobban konfigurálható mint a második, ezzel tetszőleges sok rabbithoz lehet kapcsolódni,
        és a működés is jobban átlátható.
      
- 
        Dokumentációk
        
      
- 
        Nem létező queue
 A rabbitMQ-hoz való kapcsolódáskor exception keletkezik ha a host, port, virtual-host, usernév, password
        hibásan van beállítva, de nem keletkezik exception ha nem létező queue-ba küldjük az üzenetet.
        Ez azért nem probléma mert a queue dinamikusan van kezelve, tehát egy nem létező queue-ba való íráskor
        a queue automatikusan létrejön, és belekerül az üzenet.
- 
        A rabbitMQ használatához ki kell egészíteni a pom.xml-t:
        
      
- 
        Amikor a rabbitMQ osztályait használjuk, szinte mindig ebből a package-ből kell importálni:
        
      
- 
        Consumer elindítása
 A channel objektumhoz kell a consumer objektumot hozzárendelni, a basicConsume() metódussal.
 A hozzárendelés után a consumer rögtön aktív, és fogadja az üzeneteket.
 Fontos hogy a 2. paraméter false legyen, így nincs automatikus ACK.
        
        ahol a consumerReader, az általunk írt üzenet fogadó.
- 
        Consumer leállítása
 A leállítás nem történik meg rögtön, ezzel a metódussal csak jelezzük a consumer-nek, hogy amint teheti álljon le.
- 
        ACK
 A consumer így tud egy üzenetre ACK választ adni, melynek hatására az üzenet törlődik a queue-ból:
- 
        NACK
 A consumer NACK-t hajt végre, és az üzenet a queue-ban marad, ha ezek egyike teljesül:
          - Exception-t dob kifelé
- Nem hajtja végre a basicAck()-t és a basicReject()-et sem
 Továbbá direkt is kiadhat NACK-t, és az utolsó boolean paraméterrel szabályozhatja,
        hogy a queue-ban maradjon-e az üzenet:
- 
        REJECT
 A consumer így tud egy üzenetre REJECT választ adni, 
        ahol a boolean paraméter szabályozza hogy a queue-ban marad-e az üzenet:
- 
        Példakódok
 
          - 
            RabbitConstant.java
 RabbitMQ kapcsolódás értékei.
- 
            RabbitController.java
 Üzenet küldése, és a MainApplication.runConsumer kapcsoló állítása, mellyel a consumer indítási
            vagy leállítási szándéka jelezhető.
- 
            ConsumerService.java
 Consumer kezelése.
 Időnként új channel-t nyit, így leszakadás esetén újra tud kapcsolódni.
 A MainApplication.runConsumer alapján elindítja, vagy leállítja a consumer-t.
- 
            ConsumerReader.java
 Consumer. Olvassa az üzeneteket, és figyel a channel nyitottságára.
 
2. módszer
    
      - 
        A Rabbit Message Queue használatához be kell illeszteni a pom.xml-be:
        
      
- 
        Az application.yml fájlba vigyük fel a message queue elérését. Beletehetjük a saját jellemzőket is, például: 
        
      
- 
        Üzenet küldése a message queue-ba:
        
        
          Ha hibás a küldés (rossz exchange név, rossz queue név, nincs jogosultság, ...), 
          nem keletkezik exception, vagyis nem lehet tudni sikeres volt-e a művelet!
        
      
- 
        Üzenet kiolvasása listener segítségével a message queue-ból, egy spring-bean osztályban.
 A példában a queue név nem fixen van beállítva, 
        hanem a MainApplication spring-bean queueName tagjából vesszük, vagyis spEL-t alkalmazunk.
- 
        A listener folyamatosan figyeli van-e üzenet a queue-ban. Az nem probléma ha a listener leáll miközben a queue-ban
        keletkeznek üzenetek. Amikor a listener újraindul, felismeri a queue-ban lévőket.
      
- 
        Amíg a listener metódus feldolgoz egy üzenetet, addig nem hívódik meg újra a metódus, tehát az üzenetek sorban
        állnak a feldolgozásra várva. Ez a default működés. 
      
- 
        Ha egy queue-hoz több listener tartozik, akkor a queue-ban keletkező üzenetet csak az egyik listener fogja megkapni.
        Tehát vigyázzunk ha ugyanaz a program több környezetben van, mert egymás elől elhalásszák az üzeneteket,
        illetve nem kiszámítható melyik fogja feldolgozni.
      
- 
        Üzenetet az exchange-nek kell küldeni, ő továbbítja egy vagy több queue-ba.
        Ha az exchange direkt módra van állítva, akkor a megnevezett queue-nak küldi, ha topic módú,
        akkor többnek. Topic-nál használható a "*" karakter.
      
- 
        Üzenetre reagálni 3 módon lehet:
        
          - 
            Nyugtázni ACK-val (acknowledge). Ekkor az üzenet törlődik a queue-ból.
 Ha a listener metódus lefut exception keletkezése nélkül, akkor az egy ACK-val egyenlő.
- 
            Nem nyugtázni NACK-val (no acknowledge). Ekkor az üzenet nem törlődik, hanem megmarad a queue-ban.
            Az üzenet a sor legvégére kerül, így csak a többi várakozó üzenet után fogja a listener ismét megkapni.
 Ha a listener metódus exception-t dob, akkor az egy NACK-val egyenlő.
- 
            Visszautasítani REJECT-tel. Ekkor az üzenet törlődik a queue-ból.
 Ha a listener metódus az AmqpRejectAndDontRequeueException-t dobja, akkor az egy REJECT-tel egyenlő.
 
- 
        A listener-ben nem szükséges Channel paramétert fogadni.
 A Channel objektum segítségével lehet manuálisan is ACK, NACK, REJECT-et kiadni a message-re.
- 
        Message listener ki/be kapcsolása
 Ehhez nem szükséges a programot leállítani, hanem pl. egy controller metódust elég meghívni.
        Ebben a metódusban a következők szükségesek:
        
        Továbbá, az application.yml fájlban beállítható, hogy program indításakor mi legyen a default, vagyis fusson-e:
redirect
    
      - 
        Ha egy controller metódus visszatérési értéke String, akkor egy "redirect:" kezdetű Stringgel küldhet 
        ki redirect-et, például:
        
          - 
            Saját alkalmazáson belüli redirect. A context path-t nem kell beleírni:
            
          
- 
            Redirect külső címre:
            
          
 
- 
        A "redirect:" konstans string megtalálható a spring keretrendszer UrlBasedViewResolver
        osztály egyik statikus tagjaként:
        
      
- 
        Ha egy controller metódus @ResponseBody annotációval van ellátva, akkor a metódusba állítsuk be 
        paraméterként a HttpServletResponse objektumot, és ennek a sendRedirect() metódusával végezhetjük 
        el a redirect-et. Ezesetben nem számít, hogy a metódus milyen visszatérési értéket ad vissza. 
        Ez a módszer lehet hogy nem korrekt, ugyanis ilyenkor általában webservice-ként működő controller metódusról van szó. 
        A kliens általában nem egy web böngésző, így a neki visszaküldött redirect-et lehet hogy nem tudja kezelni.
      
- 
        Ha a redirect címhez paramétereket is szeretnénk hozzáfűzni, akkor a RedirectAttributes osztályt kell használni.
      
RedirectAttributes
    
      - 
        Http redirect esetén használatos (akár alkalmazáson belüli lapra, akár külső lapra navigálunk), 
        ha a meghívott lapnak paramétereket szeretnénk átadni.
      
- 
        A RedirectAttributes objektumhoz a controller metódus paramétereként férünk hozzá.
      
- 
        A RedirectAttributes gondoskodik a paraméterek url-escape -eléséről is.
      
- 
        Az átadott paraméter lehet normál paraméter, ami az url-ben meg fog jelenni, ez alkalmazáson belüli, 
        és azon kívüli url-re is működik. 
      
- 
        Az átadott paraméter lehet flash paraméter ami nem jelenik meg az url-ben, ennek csak alkalmazáson belüli 
        redirect-nél van értelme.
        Flash paraméter esetén a hívott lap ezeket a paramétereket a Model objektumba automatikusan megkapja. 
        Akkor használjuk ha nem akarjuk hogy a felhasználó lássa az adatot az url-ben, vagy nem fér be az adat az url-be.
      
- 
        A flash paraméter azért is hasznos, mert az a módszer nem működik, hogy az egyik controller feltölti 
        a Model objektumot, redirect-ál egy másik controllerhez, az pedig kiolvasná mit rakott az előző controller a Model-be.
      
- 
        Példa hívóra:
        
        példa a meghívottra:
        
      
- 
        Angular kliens esetén az url # karaktert szokott tartalmazni. Ezesetben ne használjuk a 
        RedirectAttributes osztályt, mert az manipulálja az url összeállítását, és el fogja tüntetni a # karaktert, 
        és az utána álló részeket. Helyette a teljes url-t manuálisan állítsuk össze:
        
      
ResponseEntity
    
      - 
        Szabályozható vele a http response code, és a http header is úgy, 
        hogy közben az eredeti objektumot is vissza tudjuk adni.
      
- 
        Az eredeti controller metódus egy Member példánnyal tért vissza, de szükség volt a http response kód beállítására is,
        így be kellett csomagolni a Member-t egy ResponseEntity objektumba:
        
      
- 
        A http header, és a http response code is be van állítva a szöveges kimenethez:
        
      
- 
        Lásd még: file download
      
RestTemplate
    
      - 
        Ezzel az osztállyal főleg rest service-t lehet hívni, de soap service hívása is lehetséges!
      
- 
        Figyelem !
 Ha a meghívott url-ben query paraméter is van, akkor az url-t template-ként kell megadni,
 melyben a paraméter értékek kapcsos zárójelben szerepelnek, és később adjuk meg ezek értékeit!
- 
        Rest service hívása http-get módon, a választ string-ben kérjük
 
- 
        Rest service hívása http-get módon, query paraméterekkel, a választ JsonNode-ban kérjük
 
- 
        Rest service hívása http-post módon, a kérést json-ben küldjük, a választ json-ben kapjuk
        
      
- 
        Rest service hívása http-get vagy http-post módon, http-header beállítással  
        
      
- 
        Soap service hívása
 A soapAction értéke megtalálható a SoapUI alkalmazásban, ha onnan sikerült megszólítani az interface-t, 
        és a Raw fülnél megnézzük a kérést.
- 
        Hibakezelés
 Lehetséges, hogy a hívott rest service értelmes választ ad, de a válasz http státusza nem 200-as.
 Ilyen esetben RestClientResponseException keletkezik, 
        és csak az exception kezelő ágban lehet a válaszhoz hozzáférni.
 Lehetséges az is, hogy egy nemlétező url kerül meghívásra, vagy tűzfal nem engedi meghívni az url-t,
        ilyen esetben pedig RestClientException keletkezik.
- 
        Timeout
 Megadható egy időtartam, melynek leteltekor exception keletkezik, ha nem sikerült lebonyolítani a kommunikációt:
        
        Ez a timeout beállítás csak erre a restTemplate példányra lesz érvényes!
sentry
    
      - 
        A sentry egy központi exception, log gyűjtő szerver, melyet egyszerre több alkalmazás tud használni.
 A sentry webes felületén tekinthetők meg az alkalmazás hibák, és log-ok.
- 
        Sentry használatának beállítása spring boot projekt esetén:
 
          - 
            A sentry webes felületén készíteni kell egy sentry-project-et, melynek keletkezik egy dsn száma.
 Ezt a számot kell beállítani a spring-es projektben, hogy ehhez a sentry-project-hez történjen a logolás...
- 
            pom.xml kiegészítése:
            
          
- 
            application.yml kiegészítése:
            
          
 
soap
    
      - 
        Mielőtt hozzákezdünk a program fejlesztéshez, ki kell derítenünk milyen XML kommunikáció zajlik.
 Tehát a WSDL-t be kell rakni a SOAP UI programba, és megnézni milyen az XML kérés, és a válasz.
 A SOAP UI programban a http log fület kell megnézni, mert az mutatja meg a valós XML-t.
 Ezután a POSTMAN programban is működésre kell bírni a soap hívást, hogy biztosak legyünk a kimenő XML-ben.
 Sokszor előfordul, hogy a soap action-t az XML header-be is bele kell rakni,
        nemcsak a http header-be.
- 
        A program fejlesztésékor ebből érdemes kiindulni: 
        Consuming a SOAP web service
 
- 
        A pom.xml-t ezekkel kell kiegészíteni:
        
          - 
            spring-boot-starter-web-services
 Soap híváshoz szükséges osztályok.
- 
            jaxws-maven-plugin
 A wsdl alapján java kódot generál. Itt állítjuk be, melyik java package-be tegye a legenerált osztályokat.
 
- 
        A @Configaration annotációval ellátott osztály kiegészítése:
              
      
- 
        Egy WSDL több soap service-t tartalmaz. Minden soap service-hez külön java kliens-t kell írni.
        Példa egy kliensre: FirstClient.java
      
- 
        A FirstClient.java hivatkozik a 
        MySoapActionCallback.java osztályra.
 Azért lehet szükség a standard SoapActionCallback osztályt leszármaztatni, mert az a soap action-t
        csak a http header-be rakja be, az XML header-be viszont nem. A MySoapActionCallback osztály viszont ezt is 
        megcsinálja. Ezenkívül ki is tudja iratni az összeállított végleges XML-t, hogy ellenőrizni tudjuk.
 
- 
        Ha a soap server érzékeny arra, hogy a soap action az XML-be is belekerüljön, de a soap kliens az XML-be nem 
        rakja bele, akkor ilyen hiba keletkezik:
              
      
- 
        A FirstClient.java pedig egy tetszőleges helyről, például egy controllerből meghívható:
              
      
spEL
    
      - 
        Az spEL, vagyis Spring Expression Language stringeken belül alkalmazható, egyik hasznos felhasználása amikor
        annotációkon belül szeretnénk dinamikus, más helyről származó adatot használni. Formátum:
                
      
- 
        Példa: @Value annotációnál null mint default érték megadása:
        
      
- 
        1. példa
 @RabbitListener annotációnál, a queue nevét a MainApplication spring-bean tartalmazza:
- 
        2. példa
 Az application.yml másodpercben tartalmazza a delay idejét. Ezt az értéket veszi fel a Property spring-bean.
 A @Scheduled annotációnál nem a fixedDelay van alkalmazva, hiszen az numerikus, oda nem írható spEL.
 Helyette a fixedDelayString van használva, és a benne szereplő spEL kifejezésben még egy 
        szorzás művelet is szerepel:
spring beans
    
      - 
        Spring bean-nek nevezzük azokat az objektum példányokat, melyeket a spring az IoC konténerben 
        (Application Context-ben) tárol.
 Ezek az osztályok a következő annotációk egyikével vannak ellátva:
        
        és szintén spring bean az olyan objektum példány, amit ezzel az annotációval ellátott metódus ad vissza:
- 
        Ha egy bean-nek nem adjuk meg a scope-ját, akkor a default scope (singleton) érvényes, vagyis egyetlen példány 
        jön létre belőle az IoC konténerben. 
      
- 
        A @Controller annotációval is bean-t definiálunk, és mivel nem szoktunk neki scope-ot megadni, 
        a default scope (singleton) érvényesül, így egyetlen példány lesz belőle az egész alkalmazásra vonatkozóan. 
        Természetesen adhatunk neki scope-ot, és akkor ennek megfelelően működik.
      
- 
        Az @Autowired annotáció csak bean-en belül használható, és más bean-re lehet vele hivatkozni.
      
- 
        Bean scope-ok megadása a következő annotációkkal javasolt:
        
      
- 
        Application scope bean sosem jár le, mindig ugyanaz a példány van, hacsak nem telepítjük újra az alkalmazást.
      
- 
        Ha a session lejár, és session scope bean-re hivatkozunk, akkor a spring automatikusan újra létrehozza a bean-t, 
        és beteszi a session-be.
        Tehát a session scope bean mindig elérhető. Legfeljebb egy új példánnyal találkozunk.
      
- 
        A bean-nek kell lennie paraméter nélküli konstruktorának, különben exception keletkezik, 
        mert a spring nem tudja példányosítani.
      
- 
        A bean csak akkor jön létre ha hivatkozunk rá, de nem elég a @Autowired, hanem tényleg hivatkozni kell a példányra.
      
- 
        @PostConstruct, @PreDestroy
 A bean létrehozás, és megszüntetés eseményt el lehet kapni ha 2 metódust készítünk, 
        és ezekkel az annotációkkal látjuk el őket.
- 
        @Lazy
 Ez az annotáció például egy controllerben alkalmazható. Lényege hogy a @Autowired-del allátott bean-t 
        ne húzza be rögtön az alkalmazás inicializálásnál. Ilyenkor még nincs request, így egy request-scope bean 
        behúzása ebben a fázisban exception-t okoz a request hiánya miatt. Így a @Lazy-vel ellátott controller 
        csak akkor lesz létrehozva, ha egy http-request által meghívásra kerül. Ekkor már van request objektum is,
        ezért a @Autowired bean is behúzásra kerülhetnek.
- 
        Bean proxy:
 Tegyük fel hogy egy controllerhez @Autowired-del be van húzva egy request bean. 
        Valójában ez csak egy bean proxy példány,
        ehhez férünk hozzá (elvileg a bean leszármazottjának kéne lennie, és csak egyetlen példányban van meg).
 
 Amikor a controller fut, a bean még nem lesz aktiválva, vagyis nem fut le a konstruktora. 
        Akkor sem fut le ha a bean-nek közvetlen módon érjük el az egyik tagját (bean.random), 
        sőt ez a tag abnormális értéket tartalmaz.
        Ez azért van mert a proxy osztályhoz férünk hozzá, és ő nem delegálja a kérést a velódi bean-hez.
 
 Ha viszont a bean-nek valamelyik metódusát hívjuk (bean.getRandom()) akkor aktiválódik a bean konstruktora. 
        Viszont ezután is fontos, hogy a bean tagjának normális várt értékét csak valamilyen metódusán keresztül 
        kaphatjuk meg.   
        Ez azért van mert a proxy osztály metódusának hívásakor delegálja a kérést a velódi bean-hez.
 
 Hogy ilyen hibát ne lehessen elkövetni, valójában a bean nem statikus tagjainak private-nak kell lenniük, 
        és a tagokhoz csak getter, setter metódusokkal kell hozzáférést adni, így a kérést a proxy delegálja a 
        valódi bean-hez.
- 
        Bean név, egyediség:
 Az IoC konténerben nem lehet ugyanolyan névvel több bean osztály!
 Ha egy bean-nek nem adunk nevet, akkor csak az osztálynévből képződik a neve kis kezdőbetűvel 
        (a package név nincs benne).
        Tehát ha létrehozzuk a package1.Proba, és a package2.Proba bean-eket akkor konfliktus lesz hiszen 
        mindkettő "proba" névvel akar bekerülni az IoC konténerbe, így az alkalmazás el sem indul. 
        Ezt elkerülhetjük ha más-más nevet adunk a két bean-nek.
- 
        Az IoC konténerben lévő bean-ek listázása:
        
      
static resource
    
      - 
        Az alkalmazásban lévő static resource-ok (css, js, image, ...) elérhetők url-en keresztül 
        a controllerek megkerülésével.
 Ehhez az application.yml fájlban definiálni kell hol találhatók ezek a könyvtárak.
 A default beállításban 4 könyvtár is szerepel:
        
        A static resource keresése az alkalmazásban csak akkor történik meg, ha az adott url-hez nincs fogadó controller!
 Ezesetben a spring.web.resources.static-locations -ben megadott könyvtárakban keres balról jobbra,
 és azt a resource-t adja vissza, melyet legelőször megtalál, és az fájl, nem pedig könyvtár.
- 
        Példa: állítsuk be az application.yml fájlban a static resource keresést 2 könyvtárra:
        
        a hívott url legyen:
        
        amennyiben nincs /css/base.css url-t kezelő controller, akkor a jar-fájl következő két könyvtárban 
        keres a spring:
        
        Amennyiben egyik fájl sem létezik, az exception handler hívódik meg.
      
swagger
    
      - 
        Egy spring boot-os alkalmazásban előszőr a rest service-eket kell elkészíteni, majd ezután kell 
        "ráereszteni" a swagger-t, hogy derítse fel az alkalmazás milyen rest service-eket tartalmaz. 
        Ehhez az alkalmazást ki kell egészíteni a swagger eszközeivel.
      
- 
        A spring boot alkalmazás lehet jar, és war is, mindkettőhöz működik ez a swagger kiegészítés.
      
- 
        A rest controller metódusoknak lehetőleg ne Object legyen a visszatérési értéke, mert akkor a swagger nem tudja
        a valódi struktúrát felderíteni, hanem valami generikusabb, például egy saját JsonResponse osztály.
      
- 
        Ha a rest controller osztály metódusait magyarázattal akarjuk ellátni, mely megjelenik majd a swagger felületén, 
        akkor a metódusok elé ilyen annotáció szükséges:
        
      
OpenAPI swagger (ajánlott)
    
      - 
        Sokkal egyszerűbb beépíteni a programba mint a Springfox swaggert, és ez már támogatja a Java17 + Spring Boot 3-at is. 
      
- 
        OpenAPI v2
        ➔ Java 17 vagy ennél magasabb java verzió esetén
      
- 
        OpenAPI v1
         ➔ Java 17-nél kisebb verzió esetén 
      
- 
        A programban csak a pom.xml -t kell kiegészíteni:
        
        és a swagger felület már hívható ezen az url-en:
        
      
Springfox swagger (kevésbé ajánlott)
    
      - 
        Nehezebb beépíteni a programba mint az OpenAPI swaggert, és nem támogatja a Java 17 + Spring Boot 3-at. 
        Ha mégis ezt építjük be egy Java 17-es projektbe, akkor exception keletkezik, melyben a HttpServlet osztályt
        nem találja, ugyanis pont a Java 17-től ez az osztály már egy új jakarta package-be került át.  
      
- 
        Az alkalmazás módosítása, hogy kapjunk egy webes swagger felületet:
        
          - 
            pom.xml kiegészítése
 
- 
            Swagger config osztály elkészítése
 
- 
            application.yml módosítása
 A swagger felületen az api json a "v2/api-doc" címen jelenik meg.
 Ennek az url-nek az átdefiniálása, és egy hiba kiküszöbölése, melynek hiányában az alkalmazás el sem indul:
- 
            Ha nem akarjuk, hogy egy interceptor lefusson a swagger által készített url-ekre, 
            akkor ezt a címet le kell tiltani az interceptor regisztrációjában:
            
          
- 
            Ha az alkalmazás módosítása elkészült, akkor elérhető a swagger felület:
 Az url végéről nem hagyható le a "/" jel!
 
systemd service
    
      - 
        Az elkészült spring boot jar-fájlt telepíthetjük egy unix szerverre úgy, hogy init.d vagy systemd 
        service-ként fusson.
 A systemd service a jobb, és modernebb módszer, ennek használata ajánlott.
 A módszer előnye hogy rendszer boot után is automatikusan elindul!
 A jar-fájl szerverre másolásán kívül, el kell készíteni egy "service leíró" fájl is, 
        például myapp.service névvel,
        és a szerver /etc/systemd/system könyvtárába kell másolni. A tulajdonosa és a csoportja is 
        "root" legyen.
- 
        Példa myapp.service fájlra, melyben az ExecStart sor a hosszúsága miatt a "\" karakterrel meg van törve:
        
        
          - 
            User=tomcat8
 Annak beállítása, hogy milyen unix user nevében fusson az alkalmazás, a példában tomcat8 nevében fut.
- 
            WorkingDirectory=...
 Mindenképpen legyen beállítva, hogy a relatív url-ek ehhez tudjanak viszonyulni.
- 
            Restart=always
 A spring alkalmazás leállása esetén (akár System.exit()-et hajt végre, akár összeomlik), a systemd service 
            újraindítja az alkalmazást. Az indítási kísérletek között eltelt időt a RestartSec szabályozza,
            a fenti példában ez 10 másodpercenként történik meg.
- 
            -Djava.security.egd=file:/dev/./urandom
 Ez mindenképp kell, különben lassan indulna el az alkalmazás, valami unix-os véletlenszám generátor miatt...
 
- 
        Ha mindkét fájl felkerült a szerverre, a service elindítható, leállítható systemd service parancsokkal.
      
- 
        systemd service leállítása és elindítása. Akkor is működik ha előtte még nem futott:
        
      
- 
        systemd service indítás:
        
      
- 
        systemd service leállítás:
        
      
- 
        systemd service beállítása úgy, hogy rendszer boot után is elinduljon:
        
      
- 
        systemd service státusz megtekintés:
        
        Státusz listázásakor látható egy "enable" vagy "disable" szócska, 
        ami jelzi hogy rendszer boot után elindulna-e a service.
      
- 
        Ha megváltozik a "service" fájl, akkor utána ez is szükséges hogy életbe lépjen a módosítás: 
        
      
- 
        systemd verziójának lekérdezése:
        
      
- 
        A {serviceName} a fenti példában "myapp" vagyis a service fájl neve, a kiterjesztés nélkül.
      
- 
        Új programverzió telepítése deploy script-tel, systemd service újraindítással
 Hogy gyorsabban lehessen egy új jar-fájlt telepíteni, érdemes készíteni egy jar telepítő deploy scriptet.
        Előszőr az elkészült jar-fájl fel kell másolni a szerverre, a saját könyvtárunkba (/home/xesj).
        Ezután el kell indítani a telepítő scriptet, ami elvégzi a szükséges teendőket: 
        a jar-fájl nevéből levágja a verziószámot, bemásolja a szükséges könyvtárba, 
        és elindítja a restart scriptet (systemctl restart):
- 
        Új programverzió telepítése a systemd service újraindítása nélkül
 Ha a program el van látva exit url-lel, vagyis ezt az url-t meghívva végrehajt egy System.exit()-et,
        akkor így lehet eljárni:
          - Felül kell írni a szerveren lévő jar-fájlt
- Meg kell hívni az alkalmazás exit url-jét
 A jar felülírásával még nem fog az új programverzió futni, hiszen az a memóriából fut.
        A System.exit() hatására viszont a program leáll, és a service újraindítja az egész JVM-et, ami beolvassa
        az új jar-t a fájlrendszerből.
unit test
    
      - 
        Ki kell egészíteni a pom.xml fájlt:
        
      
- 
        Nem kell kiegészíteni a pom.xml-t a junit-ra vonatkozó bejegyzésekkel, 
        mivel a spring-boot-starter-test betölti a junit függőségeket!
      
- 
        Példa egy teszt osztályra, melyet olyan környezetben szeretnénk futtatni,
        mintha az alkalmazás a következő paraméterekkel indult volna:
        
        Ebben az esetben a teszt osztályt a következő annotációkkal kell ellátni:
        
        Az @ExtendWith a spring - junit összekapcsolása miatt szükséges. 
        
 
 A @SpringBootTest annotációnál meg kell adni a fő osztályt (vagy osztályokat), 
        mely alapján a spring megtalálja a többi komponenst. Érdemes azt az osztályt megadni,
        melyben szerepel a @SpringBootApplication annotáció, mert az rámutat a komponensek gyökér könyvtárára.
 Itt adhatók meg a parancssor egyéb kapcsolói is, vagy a spring property-k (--).
 Ha nem spring alkalmazást tesztelünk, mert például csak egy library-t írtunk a spring-hez,
        akkor készíthető egy @Configuration annotációval ellátott osztály, mely csak a teszt-nek szóló, 
        és erre kell hivatkozni.
 
 Az @ActiveProfiles annotációval beállíthatók a teszt futásakor aktív profile-ok.
 
 Ezek az annotációk biztosítják, hogy a teszt osztály spring-es környezetben fusson, 
        így például a teszt osztályban használható a szokásos @Autowired annotáció is.
 
 Ha az embedded szervert is el akarjuk indítani a teszt futásakor, akkor ezt is be kell állítani:
- 
        Ha http-hívásokkal is szeretnénk tesztelni az alkalmazást, akkor jól használható a MockMvc osztály:
        
        De használható az ismerős RestTemplate osztály is, melyhez a spring biztosít egy teszt-hez készült példányt, 
        amit behúzhatunk, és használhatjuk:
        
      
- 
        Egy alkalmazásban nagyon sok teszt osztály lehet, felesleges mindegyikhez felvenni
        ezeket az annotációkat:
 @SpringBootTest, @ActiveProfiles, @AutoConfigureMockMvc, ...
 Ráadásul az is lehetséges, hogy módosítani kell az @ActiveProfiles paraméterén, 
        amit nem akarunk rengeteg helyen átírni.
 Ezért érdemes készíteni egy saját annotációt, például TestSet néven, amelyet annotálunk a fent említett 
        annotációkkal.
 Így egy tesztelő osztályt csak ezzel a @TestSet annotációval kell ellátni, így ő megörökli a többi annotációt is. 
        A TestSet.java:
- 
        Netbeans-ben a teszt osztályok neveinek Test-re kell végződniük, 
        különben a Clean and Build nem veszi őket figyelembe, nem futtatja le a teszteket.
      
- 
        Dokumentáció: Spring Boot refdoc 45. fejezet: Testing
      
utility
    
      - 
        A spring utility-ket ez a package tartalmazza: org.springframework.util
 Azonban ezen osztályok nagy része belső használatra készült, nem biztos hogy jó ötlet ezekre építeni,
        mert változhatnak. Vannak kivételek, lásd lejjebb.
- 
        FileSystemUtils.deleteRecursively(...)
 Fájl vagy könyvtár törlése.
 
 Könyvtár esetén a teljes belső tartalmát is törli.
 Nem probléma ha nem létezik a fájl vagy könyvtár, emiatt nem dob exception-t.
 
- 
        FileSystemUtils.copyRecursively(forrás, cél)
 Fájl vagy könyvtár másolása.
 
 Fájl másolásakor:
 Ha a cél már létezik, akkor FileAlreadyExistsException-t dob.
 
 Könyvtár másolásakor:
 A forrás könyvtár teljes belső tartalmát másolja, de a forrás könyvtárat nem.
 Ha a cél könyvtár nem létezik, akkor létrehozza, az összes szülő könyvtárával együtt.
 Ha a cél könyvtár már létezik, akkor sem dob exception-t.
 A cél könyvtár tartalmát nem törli a másolás előtt.
 
validation / @
    
      - 
        Az annotációval jelölt bean validáció lényege, hogy a form bean tagjait, vagy az egész osztályt 
        annotációval látunk el, mely automatikusan validációt jelent a form bean számára. 
        Ezeket az annotációkat nevezzük validációs annotációknak.
      
- 
        Ha a standard JavaEE validációs annotációkat szeretnénk használni, akkor a "javax.validation.constraints" 
        package-en belüli annotációk szükségesek. Ezen kívül van még hibernate-es validációs annotáció is, 
        ami nem a standard JavaEE része!
      
- 
        A validáció aktiválásához az is szükséges, hogy a form post-ot fogadó controller metódusban használjuk a 
        @Valid annotációt:
        
      
- 
        A validációs annotációknak van egy "message" paramétere, melyben beállítható mi legyen a hibaüzenet 
        ha az adat hibásnak minősül.
 A "message" többféleképpen használható:
          - 
            1. lehetőség: Az annotációnak egy konkrét hibaüzenet van megadva:
            
          
- 
            2. lehetőség: A hibaüzenet a classpath gyökerében lévő ValidationMessages.properties 
            fájlban van megadva (i18n lehetőség), és az annotációban erre hivatkozunk:
 Annotáció:
            
            ValidationMessages.properties:
- 
            3. lehetőség: A hibaüzenet a classpath gyökerében lévő ValidationMessages.properties fájlban 
            van megadva (i18n lehetőség), de az annotáció nem hivatkozik rá, hanem a default kulccsal olvassa ki onnan
            a hibaüzenetet. A default kulcs az osztály teljes neve + ".message" szöveg. 
            A default kulcs megtekinthető az annotáció forráskódjában is a "message" résznél.
 Annotáció:
            
            ValidationMessages.properties:
 
- 
        Lehet olyan bean validatort is készíteni, mely nem a bean egy mezőjére, hanem több mezőre egyszerre vonatkozik,
        így az egész bean-re kell tenni az annotációt, lásd google: "bean validation multiple fields"
      
validation / standard
    
      - 
        A standard validáció a spring eredeti validációs módszere, használatához a lent leírtak szükségesek. 
      
- 
        Implementálni kell a "Validator" interface-t.
        Az interface supports() metódusában jelezzük, hogy a validátor milyen osztályt képes validálni. 
        Ez nem azt jelenti, hogy automatikusan hozzá van rendelve a validátor ehhez az osztályhoz. 
        Azt jelenti hogy amikor pl. egy @InitBinder-rel a validátort egy form bean-hez rendeljük, 
        ellenőrzésre kerül hogy a supports() metódus által támogatva van-e. Ha nem akkor exception váltódik ki! 
        
      
- 
        Be kell jegyezni a validátort a használni kívánt controllerben, annak az @InitBinder annotációval 
        ellátott metódusánál. Az @InitBinder-ben megadott névnek meg kell egyezni a @ModelAttribute-ban megadott 
        névvel hogy fusson a validátor.
        
      
- 
        A validáció aktiválásához szükséges, hogy a form post-ot fogadó controller metódusban használjuk a 
        @Valid annotációt:
        
      
- 
        A standard validációt kényelmesen használni form-ok esetében lehet.
 Webservice esetén, ha együtt használjuk a @Valid @RequestBody annotációt, a nehézségek a következők:
          - 
            A különálló osztályba helyezett validátor ugyan bejegyzi a talált hibákat, de a @Valid miatt 
            MethodArgumentNotValidException keletkezik, amit egy exception handlerben kell lekezelni.
          
- 
            Az eredeti validált objektumhoz hozzá lehet férni, de a hibaüzenetek kiolvasása a BindingResult 
            objektumból nem egyszerű.
 A példában csak a legelső globális hibaüzenetet nyerjük vissza:
 
war file
    
      - 
        Egy Spring boot alkalmazás normális esetben jar fájl-ként fut embedded server módon,
        azonban ha szükséges készíthető belőle war fájl is, és telepíthető egy alkalmazás szerverre.
      
- 
        A "create a deployable war file"
        dokumentáció részletesen leírja a war készítést, mely a következő lépésekből áll:
        
          - 
            A pom.xml-ben ki kell cserélni a csomagolás módját "jar"-ról "war"-ra:
            
          
- 
            A pom.xml-ben a "spring-boot-starter-tomcat" bejegyzéshez be kell állítani, hogy a végső fájlba 
            ne csomagolja be ezt a könyvtárat, mert a futtatókörnyezet fogja biztosítani:
                        
          
- 
            A @SpringBootApplication annotációt tartalmazó osztálynak egy SpringBootServletInitializer 
            osztály leszármazottnak kell lennie, és felül kell definiálni a configure() metódusát.
          
 
- 
        WAR esetén a futtató szervertől függ, hogy json vagy xml kimenetet generál a spring, amikor objektum a 
        controller metódus visszatérési értéke.
 Ez a bizonytalanság megszüntethető, ha például beállítjuk a @RequestMapping produces attribútumát.
-  
        Amennyiben a szerver biztosítja az adatbázis jndi nevet, és ezt nem tudjuk felvenni az application.yml 
        fájlból, akkor az egyik lehetséges megoldás a DataSource bean ilyen megírása:
        
      
websocket
    
      - 
        A websocket használatához a pom.xml bővítése szükséges:
        
      
- 
        
          dokumentáció
        
      
xesj standard
    
      - 
        Használt alap technológiák
        
          - Spring Boot
- Thymeleaf
- JdbcTemplate (DAO, DTO)
 
- 
        Projekt struktúra
        
          - 
            Root-package:
            
          
- 
            A root-package-ben nincsenek java osztályok, csak package-ek.
          
- 
            A további java osztályok csak a {root-package} alcsomagjaiban lehetnek.
          
- 
            Az alkalmazást a Run.java futtatja, mely a {root-package}.system package-ben van.
            A Run.java osztályhoz tartozik a @SpringBootApplication annotáció:
            
          
 
- 
        Controller
        
          - 
            Controller osztályok nevei: XxxController 
          
- 
            A "/" url-t lekezelő controller a MainController mely a {root-package}.system package-ben van.
          
- 
            Az egyetlen error controller szintén a {root-package}.system package-ben van, deklarációja:
            
          
 
- 
        View
        
          - 
            A thymeleaf view-k helye az "src/main/resources/view" könyvtár, és alkönyvtárai.
 Mivel nem ez a default, ezt be kell állítani az application.yml fájlban:
- 
            A controllereknek a view megadásakor meg kell adniuk a view kiterjesztését is, például: /base/menu.html
 Ezért nincs suffix:
- 
            Minden formnál kötelező kitölteni az action tag-et, például:
            
          
 
- 
        Validation
 Általam készített validátor osztályok használata.
        Webservice esetén a spring default validátor és a bean validátor használata, és a hibák kezelése nagyon nehézkes, 
        lásd: @Valid @RequestBody együtt.
- 
        Conversion
 PropertyEditor használata, a formatter-rel és converter-rel szemben. 
        A PropertyEditor üres form mezőre is fut, így ennek konvertálása is szabályozható.
- 
        Static resource
 Url-en keresztül, a controllerek kikerülésével elérhető static resource-ok 
        csak az "src/main/resources/public/static" könyvtárban és alkönyvtáraiban lehetnek.
        Mivel nem ez a default, ezt be kell állítani az application.yml fájlban:
        
        Így a rájuk való hivatkozásnál meg kell adni a "/static" könyvtárat is, melyből látszik hogy 
        statikus resource-ról van szó, továbbá a program sosem tartalmaz "/static" url-t kezelő controllert.
- 
        Context path
 Nem lehet üres.
- 
        Jar
 Csak JAR futtatás lehetséges, WAR nem használható.
- 
        Spring Security
 Nem használt, mivel feleslegesen túlbonyolított.
yaml
    
      - 
        A program jellemzők beállításához application.yml (yaml) fájlt használjunk az application.properties helyett.
        
 Nem kell külön semmit beállítani, a spring boot be fogja olvasni.
- 
        A yaml jellemzőit legjobb ha egy @ConfigurationProperties osztályba 
        olvassuk be, mert az képes a yaml-ból az összetett adatszerkezeteket (List, Map) is beolvasni.
      
- 
        A yaml előnyei:
        
          - 
            UTF-8 kódolású, tehát egyszerűen olvasható a tartalma egy sima editorral is. A properties nem UTF-8 kódolású.
          
- 
            A stringeket be lehet tenni idézőjelbe (és aposztróf közé is), így nem fordul elő, 
            hogy egy nem látható space van a végén. 
            A properties-nél előfordulhat.
 
- 
            Az idézőjeles stringen belül használhatók speciális karakterek is, például: \" \n \t
          
- 
            Egymásba ágyazhatók a tagok vagyis struktúrált, így pl. az adatbázis elérésnél nem kell mindig kiírni a prefixet. 
            A properties-nél ki kell írni.
          
- 
            Használhatjuk az egymásba ágyazást, vagy a pont-tal jelölt formát is, a kettő egyenértékű:
            
            VAGY