Tuesday, July 29, 2014

How @ModelAttribute Annotation works in Spring?

In SpringMVC, @ModelAttribute is used at 2 places.
1. At Method level
2. At Method parameter level.
How @ModelAttribute at method level works?

1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
package com.customer.controller;

import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.support.SessionStatus;

import com.customer.model.Customer;

@Controller
@RequestMapping("/customer.htm")
public class CustomerController{
 
 @RequestMapping(method = RequestMethod.POST)
 public String processSubmit(@ModelAttribute("customer") Customer customer,
   BindingResult result, SessionStatus status) {
  System.out.println("Parameter Level ModelAttriute is executed");
  if (result.hasErrors()) {
   return "CustomerForm";
  } else {
   status.setComplete();
   return "CustomerSuccess";
  }
 }
 
 @RequestMapping(method = RequestMethod.GET)
 public String initForm(ModelMap model){
  System.out.println("initForm method is executed (GET)");
  Customer cust = new Customer();
  cust.setFavFramework(new String []{"Spring MVC"});
  cust.setSex("M");
  cust.setJavaSkills("Hibernate");
  cust.setSecretValue("I'm hidden value");
  model.addAttribute("customer", cust);
  return "CustomerForm";
 }
 
 
 @ModelAttribute("webFrameworkList")
 public List<String> populateWebFrameworkList() {
  System.out.println("Method Level ModelAttriute is executed (populateWebFrameworkList)");
  List<String> webFrameworkList = new ArrayList<String>();
  webFrameworkList.add("Spring MVC");
  webFrameworkList.add("Struts 1");
  webFrameworkList.add("Struts 2");
  webFrameworkList.add("JSF");
  webFrameworkList.add("Apache Wicket");
  return webFrameworkList;
 }
 
 @InitBinder
 public void initBinder(WebDataBinder binder) {
  System.out.println("initBinder method is executed");
  SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
 }
 
 @ModelAttribute("numberList")
 public List<String> populateNumberList() {
  System.out.println("Method Level ModelAttriute is executed (populateNumberList)");
  List<String> numberList = new ArrayList<String>();
  numberList.add("Number 1");
  numberList.add("Number 2");
  numberList.add("Number 3");
  numberList.add("Number 4");
  numberList.add("Number 5");
  return numberList;
 }
 
 @ModelAttribute("javaSkillsList")
 public Map<String,String> populateJavaSkillList() {
  System.out.println("Method Level ModelAttriute is executed (populateJavaSkillList)");
  Map<String,String> javaSkill = new LinkedHashMap<String,String>();
  javaSkill.put("Hibernate", "Hibernate");
  javaSkill.put("Spring", "Spring");
  javaSkill.put("Apache Wicket", "Apache Wicket");
  javaSkill.put("Struts", "Struts");
  return javaSkill;
 }

 @ModelAttribute("countryList")
 public Map<String,String> populateCountryList() {
  System.out.println("Method Level ModelAttriute is executed (populateCountryList)");
  Map<String,String> country = new LinkedHashMap<String,String>();
  country.put("US", "United Stated");
  country.put("CHINA", "China");
  country.put("SG", "Singapore");
  country.put("MY", "Malaysia");
  return country;
 }
 
}

Explanation:
if I access this URL: http://localhost:5060/SpringMVC/customer.htm then if we find the
logging details in the console, then we can get the clear cut picture of @ModelAttribute flow


Console Output
 
This Shows that it follows 3 rules
Rule-1: If the @ModelAttribute is used at Method level, then those methods are executed first, remaining methods (which are not annotated with @ModelAttribute) are executed with the help of @RequestMapping annotation.

Rule-2: If more than one method in the controller are annotated with the @ModelAttribute, then the execution follows the sequence order of the @ModelAttribute annotated methods. (Check the output)

Rule-3: If the method parameter is annotated with @ModelAttribute, those methods are executed w.r.t @RequestMapping annotation. Ex: During the submit button execution (in controller it is POST)


Work Flow


During the execution of the handler method, you see CommandObject has been added to the Spring model attributes, but it is not yet in the HttpServletRequest or HttpSession scope.

But after the handler method has executed and when the nextpage.jsp is rendered, you can see that the model attribute data (CommandObject) has indeed been copied as an attribute (with the same attribute key) to both HttpServletRequest and HttpSession. 


Note-1: No need to write any code to set the model attribute into the request scope.

Note-2: we need to use @SessionAttributes at the class level in the controller class to set the model attributes into the session scope. 


How @ModelAttribute at method parameter level works?

Case-1: jsp to controller

Case-2: Controller to jsp

No comments:

Post a Comment