A munkamenet attribútumai a tavaszi MVC-ben

1. Áttekintés

A webalkalmazások fejlesztésekor gyakran ugyanazokra az attribútumokra kell hivatkoznunk több nézetben. Például lehetnek bevásárlókosár-tartalmaink, amelyeket több oldalon kell megjeleníteni.

Az attribútumok tárolására jó helyen van a felhasználó munkamenete.

Ebben az oktatóanyagban egy egyszerű példára és vizsgálja meg a munkamenet attribútummal való munkavégzés 2 különböző stratégiáját:

  • Határozott proxy használata
  • Használni a @SessionAttributes annotáció

2. Maven Setup

A Spring Boot indítókat használjuk a projektünk indításához és az összes szükséges függőség behozatalához.

A beállításhoz szülői nyilatkozat, webindító és timeleaf indító szükséges.

A rugós tesztindítót is beépítjük, hogy további teszteket nyújtsunk egységeink tesztjeihez:

 org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot- indító-teszt teszt 

Ezen függőségek legfrissebb verziói a Maven Central oldalon találhatók.

3. Példa használati esetre

Példánk egy egyszerű „TODO” alkalmazást valósít meg. Létezik egy űrlap a példányok létrehozására TodoItem és egy listanézet, amely az összeset megjeleníti TodoItems.

Ha létrehozunk egy TodoItem Az űrlap használatával az űrlap későbbi hozzáférései előre feltöltésre kerülnek a legutóbb hozzáadott értékekkel TodoItem. Majd használjuk tjellemzője, hogy bemutassa, hogyan lehet „emlékezni” a formai értékekre amelyek a munkamenet hatókörében vannak tárolva.

2 modellosztályunk egyszerű POJO-ként valósul meg:

public class TodoItem {private String description; privát LocalDateTime createDate; // szerelők és beállítók}
nyilvános osztály A TodoList kiterjeszti az ArrayDeque-t {}

A mi Feladatlista osztály kiterjed ArrayDeque hogy a legutóbb hozzáadott elemhez kényelmesen hozzáférhessünk a peekLast módszer.

Szükségünk lesz 2 vezérlő osztályra: 1 az egyes megvizsgálandó stratégiákhoz. Finoman különböznek egymástól, de az alapvető funkcionalitás mindkettőben megjelenik. Mindegyiknek 3 lesz @RequestMappings:

  • @GetMapping (“/ form”) - Ez a módszer felelős az űrlap inicializálásáért és az űrlapnézet rendereléséért. A módszer előre kitölti az űrlapot a legutóbb hozzáadottakkal TodoItem ha a Feladatlista nem üres.
  • @PostMapping (“/ form”) - Ez a módszer felelős a beküldött adatok hozzáadásáért TodoItem hoz Feladatlista és átirányítja a lista URL-jére.
  • @GetMapping (“/ todos.html”) - Ez a módszer egyszerűen hozzáadja a Feladatlista hoz Modell megjelenítéséhez és a listanézet rendereléséhez.

4. Scoped Proxy használata

4.1. Beállít

Ebben a beállításban a mi Feladatlista munkamenet hatókörűként van konfigurálva @Bab amelyet egy meghatalmazott támogat. Az a tény, hogy a @Bab a proxy azt jelenti, hogy képesek vagyunk beinjektálni a szingulett hatókörünkbe @Vezérlő.

Mivel a kontextus inicializálásakor nincs munkamenet, a Spring létrehozza a Feladatlista függőségként beadni. A célpéldány Feladatlista szükség szerint példányosítják, amikor a kérések megkövetelik.

A babszem hatókörének alaposabb megvitatásához tavasszal tekintse meg a témáról szóló cikkünket.

Először meghatározzuk a babunkat az a-n belül @ Konfiguráció osztály:

@Bean @Scope (value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) public TodoList todos () {return new TodoList (); }

Ezután kijelentjük, hogy a bab a @Vezérlő és ugyanúgy beadja, mint bármely más függőséget:

@Controller @RequestMapping ("/ scopedproxy") nyilvános osztály TodoControllerWithScopedProxy {private TodoList todos; // konstruktor és kérés leképezések} 

Végül, ha a babot egy kérésben használjuk, egyszerűen meg kell hívni a módszereit:

@GetMapping ("/ form") public String showForm (Model model) {if (! Todos.isEmpty ()) {model.addAttribute ("todo", todos.peekLast ()); } else {model.addAttribute ("todo", új TodoItem ()); } return "scopedproxyform"; }

4.2. Egység tesztelése

Annak érdekében, hogy teszteljük megvalósításunkat a hatókörű proxy segítségével, először konfiguráljuk a SimpleThreadScope. Ez biztosítja, hogy egységtesztjeink pontosan szimulálják az általunk tesztelt kód futási idejét.

Először meghatározzuk a TestConfig és a CustomScopeConfigurer:

@Configuration public class TestConfig {@Bean public CustomScopeConfigurer customScopeConfigurer () {CustomScopeConfigurer configurer = new CustomScopeConfigurer (); configurer.addScope ("session", új SimpleThreadScope ()); visszatérési konfigurátor; }}

Most azzal kezdhetjük, hogy teszteljük, hogy az űrlap kezdeti kérése tartalmaz-e inicializálatlan kérelmet TodoItem:

@RunWith (SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc @Import (TestConfig.class) nyilvános osztály TodoControllerWithScopedProxyIntegrationTest {// ... @Test public void whenFirstRequest_thenContainsUmformial. form ")) .andExpect (status (). isOk ()) .andExpect (model (). attributeExists (" todo ")) .andReturn (); TodoItem item = (TodoItem) result.getModelAndView (). GetModel (). Get ("todo"); assertTrue (StringUtils.isEmpty (item.getDescription ())); }} 

Megerősíthetjük azt is, hogy a beküldött küldeményünk átirányítást ad ki, és hogy egy későbbi űrlapkérés előre feltöltött lesz az újonnan hozzáadottakkal TodoItem:

@Test public void whenSubmit_thenSubsequentFormRequestContainsMostRecentTodo () dobja a (z) {mockMvc.perform (post ("/ scopedproxy / form") .param ("description", "newtodo")) .andExpect (status (). Is3xxRedirection ()) kivételt. ; MvcResult result = mockMvc.perform (get ("/ scopedproxy / form")) .andExpect (status (). IsOk ()) .andExpect (model (). AttributeExists ("todo")) .ésReturn (); TodoItem item = (TodoItem) result.getModelAndView (). GetModel (). Get ("todo"); assertEquals ("newtodo", item.getDescription ()); }

4.3. Vita

A hatókörű proxy stratégia használatának egyik legfontosabb jellemzője az nincs hatása a kérelem feltérképezési módszer aláírására. Ez nagyon magas szinten tartja az olvashatóságot a @SessionAttributes stratégia.

Hasznos lehet felidézni, hogy a vezérlőknek van szingli hatókör alapértelmezés szerint.

Ez az oka annak, hogy meg kell használnunk egy proxyt, ahelyett, hogy egyszerűen beadnánk a nem proxiált munkamenet hatókörű babot. Nem adhatunk be kisebb hatókörű babot nagyobb hatókörű babba.

Ennek megkísérlése ebben az esetben kivételt váltana ki az alábbiakat tartalmazó üzenettel: A „munkamenet” hatókör nem aktív az aktuális szálnál.

Ha hajlandóak lennénk meghatározni a vezérlőnket a munkamenet hatókörével, elkerülhetnénk a proxyMode. Ennek hátrányai lehetnek, különösen, ha a vezérlő létrehozása drága, mert minden felhasználói munkamenethez létre kellene hozni egy vezérlőpéldányt.

Vegye figyelembe, hogy Feladatlista injekcióhoz való egyéb komponensek számára elérhető. Ez a felhasználási esettől függően előny vagy hátrány lehet. Ha a komponens hozzáférhetővé tétele a teljes alkalmazás számára problémás, akkor a példány a vezérlőhöz használható @SessionAttributes amint a következő példában látni fogjuk.

5. A @SessionAttributes Megjegyzés

5.1. Beállít

Ebben a beállításban nem definiáljuk Feladatlista mint tavaszi irányítású @Bab. Ehelyett mi nyilvánítsa a @ModelAttribute és adja meg a @SessionAttributes kommentár a vezérlő munkamenetéig.

Az első alkalommal, amikor vezérlőnkhöz férnek hozzá, Spring példányt állít elő, és elhelyezi azt a Modell. Mivel a babot is ben deklaráljuk @SessionAttributes, Spring tárolja a példányt.

A @ModelAttribute tavasszal olvassa el a témával foglalkozó cikkünket.

Először deklaráljuk a babunkat azzal, hogy metódust adunk a vezérlőn, és a módszerrel jegyzeteljük @ModelAttribute:

@ModelAttribute ("todos") nyilvános TodoList todos () {return new TodoList (); } 

Ezután tájékoztatjuk az irányítót a kezelésünkről Feladatlista mint a munkamenet hatóköre @SessionAttributes:

@Controller @RequestMapping ("/ sessionattributes") @SessionAttributes ("todos") nyilvános osztály TodoControllerWithSessionAttributes {// ... egyéb módszerek}

Végül, hogy a babot egy kérésen belül felhasználhassuk, utalást adunk rá az a metódus aláírásában @RequestMapping:

@GetMapping ("/ form") public String showForm (Model model, @ModelAttribute ("todos") TodoList todos) {if (! Todos.isEmpty ()) {model.addAttribute ("todo", todos.peekLast ()) ; } else {model.addAttribute ("todo", új TodoItem ()); } return "sessionattributesform"; } 

Ban,-ben @PostMapping módszerrel injekciózunk RedirectAttributes és hívjon addFlashAttribute mielőtt visszaadná a mi RedirectView. Ez fontos különbség a megvalósításban az első példánkhoz képest:

@PostMapping ("/ form") nyilvános RedirectView create (@ModelAttribute TodoItem todo, @ModelAttribute ("todos") TodoList todos, RedirectAttributes attribútumok) {todo.setCreateDate (LocalDateTime.now ()); todos.add (todo); attributes.addFlashAttribute ("todos", todos); return new RedirectView ("/ sessionattributes / todos.html"); }

Tavasz használ egy speciális RedirectAttributes végrehajtása Modell az URL-paraméterek kódolását támogató átirányítási forgatókönyvekhez. Az átirányítás során a Modell általában csak akkor érhető el a keretrendszer számára, ha szerepelnek az URL-ben.

Használva addFlashAttribute azt mondjuk a keretnek, hogy szeretnénk a magunkét Feladatlista hogy túlélje az átirányítást anélkül, hogy kódolni kellene az URL-ben.

5.2. Egység tesztelése

Az űrlap nézetvezérlő módszer egység tesztelése megegyezik azzal a teszttel, amelyet az első példánkban megvizsgáltunk. A teszt a @PostMappingazonban egy kicsit más, mert a viselkedés igazolásához hozzá kell férnünk a flash attribútumokhoz:

@Test public void whenTodoExists_thenSubsequentFormRequestContainsesMostRecentTodo () dobja a (z) {FlashMap flashMap = mockMvc.perform (post ("/ sessionattributes / form") .param ("description", "newtodo") .param ("description", "newtodo") .andExpect (status ()). andReturn (). getFlashMap (); MvcResult result = mockMvc.perform (get ("/ sessionattributes / form") .sessionAttrs (flashMap)) .andExpect (status (). IsOk ()) .andExpect (model (). AttributeExists ("todo")). ÉsReturn ( ); TodoItem item = (TodoItem) result.getModelAndView (). GetModel (). Get ("todo"); assertEquals ("newtodo", item.getDescription ()); }

5.3. Vita

A @ModelAttribute és @SessionAttributes az attribútum munkamenetben való tárolásának stratégiája egyenes megoldás, amely nem igényel további kontextuskonfigurációt vagy Spring-kezelt @Babs.

Első példánkkal ellentétben injekciózásra van szükség Feladatlista ban,-ben @RequestMapping mód.

Ezenkívül a flash attribútumokat kell felhasználnunk az átirányítási forgatókönyvekhez.

6. Következtetés

Ebben a cikkben a hatókörű proxyk és @SessionAttributes mint 2 stratégia a munkamenet attribútumokkal való munkavégzéshez a Spring MVC-ben. Vegye figyelembe, hogy ebben az egyszerű példában a munkamenetben tárolt attribútumok csak a munkamenet életében maradnak fenn.

Ha meg kell tartanunk az attribútumokat a szerver újraindítása vagy a munkamenet időtúllépése között, akkor fontolóra vehetjük a Spring Session használatát az információk átlátható kezeléséhez. Tekintse meg a Tavaszi munkamenet cikkünket további információkért.

Mint mindig, a cikkben használt összes kód elérhető a GitHubon.