תכונות מושב באביב MVC

1. סקירה כללית

בעת פיתוח יישומי אינטרנט, לעתים קרובות אנו צריכים להתייחס לאותן תכונות בכמה תצוגות. לדוגמא, ייתכן שיש לנו תוכן של עגלת קניות שצריך להציג במספר עמודים.

מיקום טוב לאחסון תכונות אלה נמצא בהפעלה של המשתמש.

במדריך זה נתמקד בדוגמה פשוטה ו- לבחון 2 אסטרטגיות שונות לעבודה עם תכונת מושב:

  • באמצעות פרוקסי מקיף
  • משתמש ב @מאפייני מושב ביאור

2. הגדרת Maven

נשתמש במתחילי האביב של האביב כדי לאתחל את הפרויקט שלנו ולהביא את כל התלות הדרושה.

ההתקנה שלנו דורשת הצהרת הורים, סטרטר אינטרנט ומתנע עלים.

אנו נכלול גם את המתנע לבדיקת האביב כדי לספק כלי עזר נוסף במבחני היחידות שלנו:

 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- מבחן מבחן התחלה 

הגרסאות האחרונות של תלות אלה ניתן למצוא ב- Maven Central.

3. דוגמא לשימוש מקרה

הדוגמה שלנו תיישם יישום פשוט "TODO". יהיה לנו טופס ליצירת מופעים של TodoItem ותצוגת רשימה המציגה הכל TodoItemס.

אם ניצור א TodoItem באמצעות הטופס, הגישות הבאות לטופס יאוכלסו מראש עם ערכי הנוספים האחרונים TodoItem. נשתמש tהתכונה שלו להדגים כיצד "לזכור" ערכי צורה המאוחסנים בהיקף ההפעלה.

שני שיעורי המודל שלנו מיושמים כ POJO פשוטים:

מחלקה ציבורית TodoItem {תיאור מחרוזת פרטי; LocalDateTime פרטי createDate; // גטרים וקובעים}
הכיתה הציבורית TodoList מרחיבה את ArrayDeque {}

שֶׁלָנוּ רשימת מטלות הכיתה מתרחבת ArrayDeque כדי לתת לנו גישה נוחה לפריט האחרון שנוסף באמצעות הצצה אחרונה שיטה.

נצטרך 2 שיעורי בקרה: 1 לכל אחת מהאסטרטגיות שנבדוק. יהיו להם הבדלים דקים, אך פונקציונליות הליבה תוצג בשניהם. לכל אחד יהיו 3 @ בקשת מיפויs:

  • @GetMapping ("/ טופס") - שיטה זו תהיה אחראית לאתחול הטופס ועיבוד תצוגת הטופס. השיטה תאכלס מראש את הטופס עם התוספת האחרונה TodoItem אם ה רשימת מטלות זה לא ריק.
  • @PostMapping ("/ טופס") - שיטה זו תהיה אחראית על הוספת המוגש TodoItem אל ה רשימת מטלות והפניה מחדש לכתובת האתר של הרשימה.
  • @GetMapping ("/ todos.html") - שיטה זו פשוט תוסיף את רשימת מטלות אל ה דֶגֶם לתצוגה ולעיבוד תצוגת הרשימה.

4. שימוש ב- proxy מצומצם

4.1. להכין

בהתקנה זו, שלנו רשימת מטלות מוגדר כ- session-scoped @אפונה שמגובה על ידי פרוקסי. העובדה כי @אפונה הוא פרוקסי פירושו שאנחנו מסוגלים להזריק אותו לסינגלטון שלנו @בקר.

מכיוון שאין הפעלה בהתחלת ההקשר, אביב ייצור פרוקסי של רשימת מטלות להזריק כתלות. מופע היעד של רשימת מטלות יתייצב במידת הצורך כנדרש על ידי בקשות.

לדיון מעמיק יותר בהיקפי שעועית באביב, עיין במאמר שלנו בנושא.

ראשית, אנו מגדירים את השעועית שלנו בתוך a @תְצוּרָה מעמד:

@Bean @Scope (value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) TodoList ציבורי todos () {להחזיר TodoList חדש (); }

לאחר מכן, אנו מכריזים על שעועית כתלות עבור @בקר ולהזריק אותו בדיוק כמו בכל תלות אחרת:

@Controller @RequestMapping ("/ scopedproxy") מחלקה ציבורית TodoControllerWithScopedProxy {פרטי TodoList פרטי; // מיפוי קונסטרוקטור ובקשות} 

לבסוף, השימוש בשעועית בבקשה פשוט כולל קריאה לשיטותיה:

@GetMapping ("/ form") showForm מחרוזת ציבורית (מודל מודל) {if (! Todos.isEmpty ()) {model.addAttribute ("todo", todos.peekLast ()); } אחר {model.addAttribute ("todo", TodoItem חדש ()); } להחזיר "scopedproxyform"; }

4.2. בדיקת יחידה

על מנת לבדוק את היישום שלנו באמצעות ה- proxy המצומצם, ראשית אנו מגדירים א SimpleThreadScope. זה יבטיח שבדיקות היחידות שלנו מדמות במדויק את תנאי זמן הריצה של הקוד שאנחנו בודקים.

ראשית, אנו מגדירים א TestConfig ו CustomScopeConfigurer:

מחלקה ציבורית @Configuration TestConfig {@Bean CustomScopeConfigurer ציבורי customScopeConfigurer () {CustomScopeConfigurer configurer = חדש CustomScopeConfigurer (); configurer.addScope ("session", SimpleThreadScope חדש ()); מגדיר חזרה; }}

כעת נוכל להתחיל בבדיקה כי בקשה ראשונית של הטופס מכילה לא מאוחסן TodoItem:

@RunWith (SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc @Import (TestConfig.class) מחלקה ציבורית TodoControllerWithScopedProxyIntegrationTest {// ... @Test public void whenFirstRequest_thenContainsUnintialisedTodo (MistcultimateTodo) טופס ")) .andExpect (status (). isOk ()). andExpect (model (). attributeExists (" todo ")). andReturn (); פריט TodoItem = (TodoItem) result.getModelAndView (). GetModel (). Get ("todo"); assertTrue (StringUtils.isEmpty (item.getDescription ())); }} 

אנו יכולים גם לאשר שההגשה שלנו מפנה להפניה מחדש ושבקשת הטופס הבאה מאוכלסת מראש עם התוספת החדשה TodoItem:

@Test הציבור בטל כאשרSubmit_thenSubsequentFormRequestContainsMostRecentTodo () זורק חריגה {mockMvc.perform (פוסט ("/ scopedproxy / form") .param ("תיאור", "newtodo")) .andExpect (סטטוס (). Is3xxRedirection ()). ; MvcResult result = mockMvc.perform (get ("/ scopedproxy / form")). AndExpect (status (). IsOk ()). AndExpect (model (). AttributeExists ("todo")). AndReturn (); פריט TodoItem = (TodoItem) result.getModelAndView (). GetModel (). Get ("todo"); assertEquals ("newtodo", item.getDescription ()); }

4.3. דִיוּן

מאפיין מרכזי בשימוש באסטרטגיית ה- proxy המצומצמת הוא אין לזה השפעה על חתימות שיטת מיפוי בקשות. זה שומר על קריאות ברמה גבוהה מאוד בהשוואה ל- @SessionAttributes אִסטרָטֶגִיָה.

זה יכול להיות מועיל להיזכר שיש לבקרים קְלָף בּוֹדֵד היקף כברירת מחדל.

זו הסיבה מדוע עלינו להשתמש ב- proxy במקום פשוט להזריק שעועית שאינה מקודדת. איננו יכולים להזריק שעועית בהיקף קטן יותר לשעועית בהיקף גדול יותר.

ניסיון לעשות זאת, במקרה זה, יפעיל חריג עם הודעה המכילה: היקף 'מושב' אינו פעיל לשרשור הנוכחי.

אם אנו מוכנים להגדיר את הבקר שלנו עם היקף הפעלה, אנו יכולים להימנע מלפרט א proxyMode. זה יכול להיות חסרונות, במיוחד אם הבקר יקר ליצירה מכיוון שיהיה צורך ליצור מופע בקר לכל הפעלת משתמש.

ציין זאת רשימת מטלות זמין לרכיבים אחרים להזרקה. זה עשוי להיות יתרון או חסרון בהתאם למקרה השימוש. אם הבעיה היא להעמיד את השעועית לזמינה עבור כל היישום, ניתן להשתמש במופע במקום זאת באמצעות הבקר @SessionAttributes כפי שנראה בדוגמה הבאה.

5. שימוש ב- @SessionAttributes ביאור

5.1. להכין

בהתקנה זו איננו מגדירים רשימת מטלות כמנהל אביב @אפונה. במקום זאת, אנחנו להכריז על זה כ- @ModelAttribute וציין את @SessionAttributes ביאור להיקפה למושב עבור הבקר.

בפעם הראשונה שנגיש לבקר שלנו, אביב יפעיל מופע וימקם אותו ב דֶגֶם. מכיוון שאנחנו מכריזים גם על השעועית @SessionAttributes, אביב יאחסן את המופע.

לדיון מעמיק יותר על @ModelAttribute באביב, עיין במאמר שלנו בנושא.

ראשית, אנו מצהירים על שעועיתנו על ידי מתן שיטה בבקר ואנו מציינים את השיטה באמצעותה @ModelAttribute:

@ModelAttribute ("todos") TodoList todos ציבורי () {להחזיר TodoList חדש (); } 

לאחר מכן, אנו מודיעים לבקר לטפל בזה שלנו רשימת מטלות כ- session-scoped באמצעות @SessionAttributes:

@Controller @RequestMapping ("/ sessionattributes") @SessionAttributes ("todos") מחלקה ציבורית TodoControllerWithSessionAttributes {// ... שיטות אחרות}

לבסוף, כדי להשתמש בשעועית בתוך בקשה, אנו מספקים התייחסות אליה בחתימת השיטה של ​​א @ בקשת מיפוי:

@GetMapping ("/ form") showForm מחרוזת ציבורי (מודל מודל, @ModelAttribute ("todos") TodoList todos) {if (! Todos.isEmpty ()) {model.addAttribute ("todo", todos.peekLast ()) ; } אחר {model.addAttribute ("todo", TodoItem חדש ()); } להחזיר "sessionattributesform"; } 

בתוך ה @ פוסט מיפוי שיטה, אנחנו מזריקים מאפייני הפניה מחדש ולהתקשר addFlashAttribute לפני החזרת שלנו RedirectView. זהו הבדל חשוב ביישום בהשוואה לדוגמא הראשונה שלנו:

@PostMapping ("/ form") RedirectView ציבורי ליצור (@ModelAttribute TodoItem todo, @ModelAttribute ("todos") TodoList todos, RedirectAttributes תכונות) {todo.setCreateDate (LocalDateTime.now ()); todos.add (todo); attributes.addFlashAttribute ("טודוס", טודוס); להחזיר RedirectView חדש ("/ sessionattributes / todos.html"); }

אביב משתמש במומחה מיוחד מאפייני הפניה מחדש הטמעה של דֶגֶם לתרחישים להפניה מחדש לתמיכה בקידוד של פרמטרים של כתובות אתרים. במהלך הפניה מחדש, כל המאפיינים המאוחסנים ב- דֶגֶם בדרך כלל יהיו זמינים למסגרת רק אם הם נכללים בכתובת האתר.

על ידי שימוש ב addFlashAttribute אנחנו אומרים את המסגרת שאנחנו רוצים את שלנו רשימת מטלות כדי לשרוד את ההפניה מחדש מבלי להזדקק לקידוד בכתובת האתר.

5.2. בדיקת יחידה

בדיקת היחידות של שיטת בקר תצוגת הטופס זהה למבחן שבחנו בדוגמה הראשונה שלנו. המבחן של @ פוסט מיפויעם זאת, הוא קצת שונה מכיוון שאנחנו צריכים לגשת לתכונות הבזק כדי לאמת את ההתנהגות:

@Test הציבור בטל כאשרTodoExists_thenSubsequentFormRequestContainsesMostRecentTodo () זורק חריג {FlashMap flashMap = mockMvc.perform (פוסט ("/ sessionattributes / form") .param ("תיאור", "newtodo")) .andExpect (סטטוס () .3) .xx. andReturn (). getFlashMap (); MvcResult result = mockMvc.perform (get ("/ sessionattributes / form") .sessionAttrs (flashMap)). AndExpect (status (). IsOk ()). AndExpect (model (). AttributeExists ("todo")). AndReturn ( ); פריט TodoItem = (TodoItem) result.getModelAndView (). GetModel (). Get ("todo"); assertEquals ("newtodo", item.getDescription ()); }

5.3. דִיוּן

ה @ModelAttribute ו @SessionAttributes אסטרטגיה לאחסון תכונה במושב היא פיתרון פשוט לא דורש תצורת הקשר נוספת או מנוהל על ידי אביב @אפונהס.

שלא כמו הדוגמה הראשונה שלנו, יש צורך להזריק רשימת מטלות בתוך ה @ בקשת מיפוי שיטות.

בנוסף, עלינו להשתמש בתכונות פלאש לתרחישים להפניה מחדש.

6. מסקנה

במאמר זה, בדקנו שימוש באופייני סקופ ו @SessionAttributes כשתי אסטרטגיות לעבודה עם תכונות הפעלה ב- MVC באביב. שים לב שבדוגמה הפשוטה הזו, כל תכונות המאוחסנות בהפעלה ישרדו רק למשך חיי ההפעלה.

אם היינו צריכים להתמיד בתכונות בין הפעלה מחדש של השרת או פסק זמן להפעלה, נוכל לשקול להשתמש באביב סשן כדי לטפל בשקיפות בשמירת המידע. עיין במאמר שלנו על מושב אביב למידע נוסף.

כמו תמיד, כל הקוד המשמש במאמר זה זמין ב- GitHub.