What are the main differences between the following Spring Framework classes?
Model
ModelMap
ModelAndView
Using Model.put(String,Object) we can access the values in .jsp files, but ModelMap.addAttribute(String,Object) also did same thing. I do not understand the difference between these classes.
Model is an interface while ModelMap is a class.
ModelAndView is just a container for both a ModelMap and a view object. It allows a controller to return both as a single value.
Differences between Model, ModelMap, and ModelAndView
Model: It is an Interface. It defines a holder for model attributes and primarily designed for adding attributes to the model.
Example:
#RequestMapping(method = RequestMethod.GET)
public String printHello(Model model) {
model.addAttribute("message", "Hello World!!");
return "hello";
}
ModelMap: Implementation of Map for use when building model data for use with UI tools.Supports chained calls and generation of model attribute names.
Example:
#RequestMapping("/helloworld")
public String hello(ModelMap map) {
String helloWorldMessage = "Hello world!";
String welcomeMessage = "Welcome!";
map.addAttribute("helloMessage", helloWorldMessage);
map.addAttribute("welcomeMessage", welcomeMessage);
return "hello";
}
ModelAndView: This class merely holds both to make it possible for a controller to return both model and view in a single return value.
Example:
#RequestMapping("/welcome")
public ModelAndView helloWorld() {
String message = "Hello World!";
return new ModelAndView("welcome", "message", message);
}
Model: is an interface it contains four addAttribute and one merAttribute method.
ModelMap: implements Map interface. It also contains Map method.
ModelAndView: As Bart explain it allows a controller return both as a single value.
Related
I'm new to Spring MVC Framework. I'm doing some self study to extend my knowledge in Java.
This is how I understand the getProducts() code definition from a tutorial I'm following but please correct me if I'm wrong.
Controller requests something from the Data Access Object >
Data Access Object gets the data from a Database or a Model through the getProductList() method > Stores the information to list > Then binds the list to the model.
So I got two question about this.
Is the inclusion of model as parameter in public String getProducts(Model model) considered the dependency injection
Is products (within quotes) in model.addAttribute("products",products); just a name which I can change to whatever I like or should it match something?
public class HomeController {
private ProductDao productDao = new ProductDao();
#RequestMapping("/")
public String home(){
return "home";
}
#RequestMapping("/productList")
public String getProducts(Model model){
List<Product> products = productDao.getProductList();
model.addAttribute("products",products);
return "productList"; //productList string is the productList.jsp which is a view
}
#RequestMapping("/productList/viewProduct")
public String viewProduct(){
return "viewProduct";
}
}
I'd appreciate any explanation or comment.
Thank you.
Yes,
model is instantiated by spring and injected to your method, means if any of model attribute matches anything in request it will be filled. and it should be the last param in the method
model.addAttribute("products",products);
"products" is just a name which you can use it in your view get the value with ${products}
My code. this is sample.
#Autowired
private ProductService productService;
#RequestMapping(value = "/settings/product")
public ModelAndView showProduct(ModelAndView mav, HttpServletRequest req, Authentication auth) {
CustomUserDetail customUserDetail = (CustomUserDetail) auth.getPrincipal();
int associationIdx = customUserDetail.getAccount().getAssociation().getIdx();
String language = CookieUtil.getCookieValue(req, "lang");
Association association = associationService.findAssociationByIdx(associationIdx);
List<AssociationProductColumnDefine> columns = associationService.findByAssociationAndLanguage(association,
language);
List<AssociationProductColumnDefine> source = associationService.findByAssociationAndLanguage(association,
"ko-kr");
mav.addObject("association", association);
mav.addObject("source", source);
mav.addObject("columns", columns);
mav.setViewName("/association/association_settings_product");
return mav;
}
Yes, you choice model and ModelAndView.
Yes, simple.
Question about Spring MVC #ModelAttribute methods, Setting model attributes in a controller #RequestMapping method verses setting attribute individually with #ModelAttribute methods, which one is considered better and is more used?
From design point of view which approach is considered better from the following:
Approach 1
#ModelAttribute("message")
public String addMessage(#PathVariable("userName") String userName, ModelMap model) {
LOGGER.info("addMessage - " + userName);
return "Spring 3 MVC Hello World - " + userName;
}
#RequestMapping(value="/welcome/{userName}", method = RequestMethod.GET)
public String printWelcome(#PathVariable("userName") String userName, ModelMap model) {
LOGGER.info("printWelcome - " + userName);
return "hello";
}
Approach 2
#RequestMapping(value="/welcome/{userName}", method = RequestMethod.GET)
public String printWelcome(#PathVariable("userName") String userName, ModelMap model) {
LOGGER.info("printWelcome - " + userName);
model.addAttribute("message", "Spring 3 MVC Hello World - " + userName);
return "hello";
}
The #ModelAttribute annotation serves two purposes depending on how it is used:
At Method level
Use #ModelAttribute at the method level to provide reference data for the model. #ModelAttribute annotated methods are executed before the chosen #RequestMapping annotated handler method. They effectively pre-populate the implicit model with specific attributes, often loaded from a database. Such an attribute can then already be accessed through #ModelAttribute annotated handler method parameters in the chosen handler method, potentially with binding and validation applied to it.
In other words; a method annotated with #ModelAttribute will populate the specified “key” in the model. This happens BEFORE the #RequestMapping
At Method Parameter level
At Method Parameter level
When you place #ModelAttribute on a method parameter, #ModelAttribute maps a model attribute to the specific, annotated method parameter. This is how the controller gets a reference to the object holding the data entered in the form.
Examples
Method Level
#Controller
public class MyController {
#ModelAttribute("productsList")
public Collection<Product> populateProducts() {
return this.productsService.getProducts();
}
}
So, in the above example, “productsList” in the Model is populated before the the #RequestMapping is performed.
Method parameter level
#Controller
public class MyController {
#RequestMapping(method = RequestMethod.POST)
public String processSubmit(#ModelAttribute("product") Product myProduct, BindingResult result, SessionStatus status) {
new ProductValidator().validate(myProduct, result);
if (result.hasErrors()) {
return "productForm";
}
else {
this.productsService.saveProduct(myProduct);
status.setComplete();
return "productSaved";
}
}
}
Look here for detailed information with examples.
One is not better then the other. They both serve another purpose.
Method: If you need the model for a particular controller to be always populated with certain attributes the method level #ModelAttribute makes more sense.
Parameter: Use it on a parameter when you want to bind data from the request and add it to the model implicitly.
To answer your question on the better approach
I would say approach 2 is better since the data is specific to that handler.
I have a method which is taking #modelattribute as the parameter and is returning model and view object as shown below
#RequestMapping(value = "/pathtorequest", method = RequestMethod.POST)
public ModelAndView redirectdemo( HttpServletRequest req,#ModelAttribute(value="demo") Employee e) {
ModelAndView m=new ModelAndView("result");
Map<String,Object> map=m.getModel();
for(String s:map.keySet()){
System.out.println("key::"+s+" value::"+map.get(s));
}
return m;
}
foreach loop is not printing anything whereas an object is added to model with name=demo.
in the view page which is result i am getting the value of modelattribute in requestScope.
Why the object demo is not added to model map? isnt the demo a model object?
Because, although the Employee object is added by the #ModelAttribute annotated parameter, you then create a brand new ModelAndView with the line
ModelAndView m=new ModelAndView("result");
Then you iterate over m which contains only a view name (i.e. "result") but no model.
When you return a modelAndView, Spring will add to it all the other model attributes created by #ModelAttribute annotations.
If you want to manilpulate the model in your method, add it as a parameter:
#RequestMapping(value = "/pathtorequest", method = RequestMethod.POST)
public ModelAndView redirectdemo( HttpServletRequest req,#ModelAttribute(value="demo") Employee e, ModelMap modelMap) {
for(String s : modelMap.keySet()){
System.out.println("key::"+s+" value::"+modelMap.get(s));
}
}
What is the most elegant/effective way, how to handle Model between Controllers in Spring MVC 3.2. For redirecting to another Controller I use forward method, so there is not necessary new instance of request and Model data should be accessible (if I am not wrong). Is there any way how to catch Model, which was added in first Controller?
(I know about RedirectAttributes, but may be is better/easier method)
Example:
#Controller
public class WebpageController{
#RequestMapping( value = { "/{code}" } )
public String handleFirstLevel(#PathVariable String code, ModelMap modelMap) throws PageNotFoundEception{
final Webpage webpage = getWebpage(code);
modelMap.put(WEBPAGE_MODEL_KEY, prepareModel(webpage));
return "forward:some-url";
}
private Map<String, Object> prepareModel(Webpage webpage){
Map<String, Object> model = new HashMap<String, Object>();
model.put("webpage", webpage);
return model;
}
// some other code
}
#Controller
public class SpecialWebpageController{
#RequestMapping( value = { "/some-url" } )
public String handleFirstLevel(#PathVariable String code, ModelMap modelMap) throws PageNotFoundEception{
// need access to previously appended model to add some other data
return "specialViewName";
}
}
Thank you
When you have a handler method that simply returns a String, that String is considered a view name. With a prefix of forward, Spring will get a RequestDispatcher for the specified path and forward to it. Part of that process will include taking the Model from the ModelAndView created for that request handling cycle and putting all its attributes into the HttpServletRequest attributes.
The Servlet container will take the RequestDispatcher#forward(..) and again use your DispatcherServlet to handle it. Your DispatcherServlet will create a new ModelAndView with a new Model for this handling cycle. Therefore this Model doesn't contain any of the attributes from before but the HttpServletRequest attributes do.
In your case, this
modelMap.put(WEBPAGE_MODEL_KEY, prepareModel(webpage));
will end up being in
HttpServletRequest request = ...;
request.getAttribute(WEBPAGE_MODEL_KEY);
At the moment I just want to test redirecting from the update method back to the sox method. But instead I get an error complaining about a missing "update.jsp".
#RequestMapping(value = "/sox/update", method = RequestMethod.POST)
#ModelAttribute("formWrapper")
public final String update(HttpServletRequest request,
#ModelAttribute("formWrapper") FormWrapper formWrapper,
BindingResult bindResult,
ModelMap model)
{
return "redirect:/sox";
}
#ModelAttribute("formWrapper")
FormWrapper setupForm()
{
FormWrapper formWrapper = new FormWrapper();
return formWrapper;
}
#RequestMapping(value = "/sox", method = RequestMethod.GET)
public final String sox(HttpServletRequest request, ModelMap model)
{
return "sox";
}
I think your problem is the #ModelAttribute on the update method. Try it this way :
#RequestMapping(value = "/sox/update", method = RequestMethod.POST)
public final String update(HttpServletRequest request,
#ModelAttribute("formWrapper") FormWrapper formWrapper,
BindingResult bindResult,
ModelMap model)
{
return "redirect:/sox";
}
When you add the #ModelAttribute to a method spring treats the return as a model attribute :
Any other return type is considered to
be a single model attribute to be
exposed to the view, using the
attribute name specified through
#ModelAttribute at the method level
(or the default attribute name based
on the return type class name). The
model is implicitly enriched with
command objects and the results of
#ModelAttribute annotated reference
data accessor methods.
Look at 15.3.2.8 of the Spring Docs for more info: (http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-modelattrib)