Itt jársz most: Kezdőlap > Alkalmazásfejlesztés > Spring Boot framework frissítése 3-as verzióra

Szűrő megjelenítése

Spring Boot framework frissítése 3-as verzióra

A Spring Boot 3-as verziója tavaly novemberben jelent meg, ez idáig azonban nem akartam belefogni a Leaflet stack frissítésébe, mivel a major verzió frissítésekhez mindig szeretek óvatosan hozzáállni. Nem is azért, mert nem bízom benne, hogy az akár immáron 6-7 éves komponensek (például a Leaflet Backend) nem bírkózna meg a váltással (oké, valójában kicsit ez is benne van), de a major verzió ugrások után tipikusan számos patch jön ki viszonylag gyors ütemben (ezek tipikusan bugfixek). Ez most sem volt másképp, havonta egy új patch verzió jelent meg a Boot-ból, így jobbnak láttam várni még egy kicsit. Aztán végül a 3.0.6-os verzióra kezdtem el pár hete a frissítést, majd a múlt héten megjelenő 3.1.0 verzióra esett végül a választásom. A frissítés sikeresen lezajlott, és bár a stack nem csak a Boot, de a Java runtime verzió tekintetében is lemaradásban volt (Java 11 volt eddig), viszonylag fájdalommentesen sikerült megoldani. A frissítés részeként a Java runtime verziót is emeltem minden kiegészítő libraryben és az alkalmazásokban is, immáron a teljes stack Java 17-en fut - ez egyébként a minimum szükséges verziója a Boot 3-nak is, így amennyiben a frissítésen gondolkodunk, de az alkalmazásunk még nem Java 17-en fut, itt az ideje.

Hogyan kezdjük el?

Na de hogyan is nézett ki a frissítés? Nos, mint azt említettem, viszonylag fájdalommentesen megoldható, bár vannak apró buktatók. Amennyiben Java 17-re átállás is része kell legyen a frissítésnek és szeretnénk elvégezni a kódon bizonyos optimalizálásokat, mindenképp azt tudom javasolni, hogy külön change-ben tegyük azt meg, sokat egyszerűsít a helyzeten (utólag okos az ember, ugyebár). Optimalizálások alatt gondolok itt például a record típusokra migrálásra, az számtalan ponton tud fejfájást okozni, mikor a getterek és setterek hivatkozásait kezdjük el kitakarítani. Amit még továbbá tudok javasolni, mielőtt hozzákezdünk a frissítéshez, frissítsük az alkalmazást a legrégebbi 2.7-es Bootra (a cikk írásának pillanatában ez a 2.7.12-es verzió). Valóban dupla-munkának hangzik, azonban a 2.5-2.6-2.7 frissítések kifejezetten veszélytelenek voltak, ugyanakkor az API számos pontja került deprekálásra, melyeket lépésenként sokkal egyszerűbb javítani, mint egyben. Ráadásul a főverziós váltásnál a Boot által biztosított API-ok egy része teljesen kikerült a frameworkből, így ha deprekált API hívásokkal vágunk bele a frissítésbe, szinte biztosan lesz pár kellemetlen meglepetésünk.

Néhány probléma, amibe belefutottam

Az alábbiakban néhány olyan problémát részleteznék, melybe belefutottam a migrálás során és tekintve, hogy a Leaflet stack komponensei nem kifejezetten komplexek és közel csak a legszükségesebb Boot-os feature-öket használják, olyan problémákról lesz szó, melyek legtöbbünket érinthetnek a frissítés során.

Jakarta API

Mivel is kezdhetném a felsorolást, ha nem a Java EE (azaz javax.*) packagek cseréjével. A Spring Boot 3 teljesen átállt a Jakarta API használatára, így a Servlet API, Validation API, standard DI (annotation) API, RS/WS API, stb. packagek neve immáron jakarta.* előtaggal kezdődik. Bármennyire is úgy érezzük, hogy ez egy szimpla, a projekt összes fájlán átívelő Find & Replace művelettel orvosolható, a helyzet azért nem ennyire egyszerű (bár majdnem). Ugyanis a Datasource API és a Crypto API (illetve még biztos vagyok benne, hogy néhány további, amiket az alkalmazásaim nem használnak), nem kapták meg ugyanezt a név-migrálást, azok továbbra is javax.* package névvel kezdődnek. Szerencsére ha erre odafigyelünk, egy teljes projektet érintő Find & Replace valóban segíthet - de azért utána csináljunk egy buildet. :)

Amennyiben használtuk a com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider (vagy bármilyen JAXRS/JAXWS) libraryt, a továbbiakban a Jakarta API átállás miatt nem fognak működni. Helyette a com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-json-providerhasználható. Ugyan emiatt a wiremock-jre8 library sem működik tovább, a wiremock-jre8-standalone verzió használható helyette (ez nyilván elsősorban az automata tesztek futtatásánál okozhat problémát).

Spring Security API változások

Ez egy kifejezetten érdekes pontja a frissítésnek, mivel ugyanazt az API-t végül kétszer kellett átírnom. Mint említettem, eleinte a 3.0.6-os verzióra szándékoztam váltani, melyben megszűnt a HttpSecurity#authorizeRequests() metódus. Pontosabban, csak deprekálva lett, a benne levő antMatcher() metódus viszont már el lett távolítva. Az érdekes az, hogy a kiinduló verzióban (Boot 2.7.8) még nem volt szó az API deprekálásáról, így valahol a .8 és a .12-es verziók között kerülhetett erre sor. A kérdéses metódusokat az authorizeHttpRequests() és a requestMatcher() metódusok váltják. A javítás azonban még itt nem ért véget, a 3.1.0-ra átállással a paramétermentes (fluent API-t adó) verziója az authorizeHttpRequests() metódusnak és gyakorlatilag vele együtt az összes többi hasonló configurer metódusnak deprekált lett, eltávolításra megjelölve, így váltanom kellett második körben az új, Customizer paraméteres metódusokra. Bosszantó a többlépéses deprekálás, de ezekre figyeljünk oda.

Ugyanitt észrevettem további apró, mégis komoly problémákat okozni tudó, viselkedésbeli változásokat is. Ilyen például az implicit security policy megváltozása. Eddig, ha nem jelentettük ki egyértelműen egy endpointról, hogy az authentikálást igényel és semmilyen authentikálást megkövetelő szabály nem illeszkedett rá, az endpoint implicit publikus maradt. A továbbiakban ez már nem így lesz, a web security engedélyezése után minden endpoint implicit védett. Ez egy szignifikáns változás, és bár a biztonságot egyértelműen növeli, nagyon komoly hatása lehet a publikusan hagyott endpointokra és egy figyelmetlen frissítés során véletlenül blokkolhatjuk az elérésüket.

Hasonló változtatást kaptak az actuator endpointok is, immáron nem elég őket a management konfigurációval expose-olni, a security konfigurációnak egyértelműen kell azokról is nyilatkoznia, tehát szükségünk lesz például egy permitAll() szabályra a health endpointhoz, ha azt például egy Docker vagy Kubernetes cluster használja az alkalmazás működőképes állapotának ellenőrzésére.

Custom auto-config implementációk

Amennyiben volt már dolgunk saját készítésű auto-konfigurációval, vagy csak biztosítani akartuk egy librarynk regisztrálását az application contextben, akkor biztosan találkoztunk már a spring.factories fájllal. Ez a fájl a továbbiakban nem létezik, ha erre támaszkodunk, nem lesz feldolgozva. Helyette, szintén a META-INF mappában kell létrehoznunk egy spring nevű mappát és abban egy org.springframework.boot.autoconfigure.AutoConfiguration.importsnevű fájlt. A fájl tartalma a betöltendő @Configuration osztályok teljes referenciája - szóval hasonlóan, mint korábban, de az ...EnableAutoConfiguration= rész már nem kell. Új lehetőség emellett az, hogy egy sima konfigurációs osztályon az @Configuration helyett az @AutoConfiguration annotációt használjuk. A viselkedése elvileg teljesen ugyanaz, mint a .imports fájlos megoldás.

Thymeleaf változások

Az egyik legszignifikánsabb és talán legproblémásabb változás a Thymeleafet érinti. Egyrészt figyeljünk oda arra, hogy a thymeleaf-spring5 és thymeleaf-extras-springsecurity5 libraryk kikerültek a Boot-ból, ezek helyett a 6-osra végződőeket használhatjuk a továbbiakban. A nagyobb problémát egyrészt az immáron deprekált korábbi layout expressionök okozzák (például fragment :: some-component helyett ~{fragment :: some-component} mostantól a szintaxis), másrészt pedig a #request, #response, #session, és #servletContext globális változók teljes megszűnése. Hangsúlyozom, ez utóbbi nem egyszerű deprekálás, hanem teljes megszüntetés, a template parser exceptionnel díjazza a feldolgozást, amennyiben belefut egy ilyenbe. Gyors workaround van, javasolni nem szeretném. Nyilván a korrekt megoldás az, hogy minden amit eddig ezekből a változókból szedtünk, legyenek átadva template paraméterként a controllerből.

További kisebb változások

  • A Mockito lenient attribútuma kapott egy deprekálást, helyett a strictness = Mock.Strictness.LENIENTattribútum használható. Továbbá a statikus mockolásra való Mockito Inline immáron szerves része a Mockito 5-ös verziójának (ez található a Boot 3-ban), így nem szükséges a továbbiakban külön importálni, a Boot Test Starter tartalmazza.
  • A Micrometer metrikák jelentős része eltörik a verziófrissítés után, mivel a Boot 3 átáll a Micrometer saját API-járól az Observability API használatára. Ez azt jelenti, hogy a korábbi @Timed és @Counted metrikák nem működnek a továbbiakban, bár a "régi" TimedAspect és CountedAspect aspectek használatával még szóra lehet őket bírni - a custom metrika tagek azonban valamiért így nem reportálódnak. Megoldást erre még sajnos nem találtam, frissítem majd a cikket, ha sikerül. (Ha nem, akkor kezdődhet az átállás az Observability API-ra.)
  • Aorg.hibernate.dialect.MySQL8Dialect megszűnt, helyette a org.hibernate.dialect.MySQLDialect használható.

Konklúzió

Nagyjából ennyi lett volna a mai cikkem, és bár talán a fent felsorolt problémák soknak tűnhetnek, annyira mégsem éreztem vészesnek a frissítést. Egy jól karbantartott alkalmazás viszonylag könnyen frissíthető, a felbukkanó problémákra pedig szerencsére már most lehet gyorsan megoldást találni. Ebben egyébként maga a Spring is segít, két dokumentáció is elérhető a frissítésről, egy a 2.7 -> 3.0 és egy másik a 3.0 -> 3.1 átállásban segít, illetve ad egy sokkal hosszabb és részletesebb listát mindazon problémákról, amikbe a frissítés során bele fogunk futni. A cikk végén mellékelem a két linket.

Spring Boot 3.0 Release Notes és frissítési útmutató

Spring Boot 3.1 Release Notes és frissítési útmutató

Kommentek

Komment írásához jelentkezz be
Bejelentkezés

Még senki nem szólt hozzá ehhez a bejegyzéshez.