I'm having a problem with @ModelAttribute usage.
Having a simple Spring (3.2.11) mvc application with such context:
<mvc:annotation-driven />
<context:component-scan base-package="spring.test" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
Having 2 DTOs: PageDTO and SessionDTO, inside these two DTOs are completely the same, containing just one property - Address address and get/set methods.
package spring.test.model;
public class PageDTO implements java.io.Serializable {
private Address address;
// getters / setters omitted
}
package spring.test.model;
public class SessionDTO implements java.io.Serializable {
private Address address;
// getters / setters omitted
}
package spring.test.model;
public class Address implements java.io.Serializable {
private String street;
private String houseNo;
private String city;
private String zip;
// getters/setters omitted
}
Having an addressForm.jsp with one form:
<c:url var="actionURL" value="/processForm"/>
<form:form method="POST" modelAttribute="pageDto" action="${actionURL}">
<form:label path="address.street">Street: </form:label>
<form:input path="address.street"/>
<form:label path="address.houseNo">House No: </form:label>
<form:input path="address.houseNo"/>
<form:label path="address.city">City: </form:label>
<form:input path="address.city"/>
<form:label path="address.zip">ZIP: </form:label>
<form:input path="address.zip"/>
<input type="submit" name="searchAddress" value="Submit" />
</form:form>
Having a controller:
package spring.test.controller;
// imports omitted
@SessionAttributes("sessionDto")
@Controller
public class FormController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
}
@ModelAttribute("pageDto")
public PageDTO initPageDTO() {
return new PageDTO();
}
@ModelAttribute("sessionDto")
public SessionDTO initSessionDTO() {
return new SessionDTO();
}
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView renderHome(@ModelAttribute("pageDto") PageDTO pageDto,
@ModelAttribute("sessionDto") SessionDTO sessionDto) {
return new ModelAndView("addressForm", "pageDto", pageDto);
}
@RequestMapping(value = "/processForm", method = RequestMethod.POST)
public ModelAndView processForm(@ModelAttribute("pageDto") PageDTO pageDto,
@ModelAttribute("sessionDto") SessionDTO sessionDto) {
if(pageDto != null && pageDto.getAddress() != null
&& sessionDto != null && sessionDto.getAddress() != null) {
System.out.println("pageDto.getAddress().getHouseNo(): " + pageDto.getAddress().getHouseNo());
System.out.println("sessionDto.getAddress().getHouseNo(): " + sessionDto.getAddress().getHouseNo());
}
return new ModelAndView("addressForm", "pageDto", pageDto);
}
}
And here comes the problem. When I submit the address form, both of the DTOs are filled in with the data from the form. I would expect only the model attribute with the "pageDto" name mentioned in the form tag in the jsp to be filled in.
Log entry from the form-submit event:
[10/24/14 11:23:09:382 CEST] 0000002b SystemOut O pageDto.getAddress().getHouseNo(): 41
[10/24/14 11:23:09:382 CEST] 0000002b SystemOut O sessionDto.getAddress().getHouseNo(): 41
When I change the processForm method in the controller like this:
@RequestMapping(value = "/processForm", method = RequestMethod.POST)
public ModelAndView processForm(@ModelAttribute("pageDto") PageDTO pageDto,
HttpSession httpSession) {
SessionDTO sessionDto = (SessionDTO) httpSession.getAttribute("sessionDto");
if(pageDto != null && pageDto.getAddress() != null
&& sessionDto != null && sessionDto.getAddress() != null) {
System.out.println("pageDto.getAddress().getHouseNo(): " + pageDto.getAddress().getHouseNo());
System.out.println("sessionDto.getAddress().getHouseNo(): " + sessionDto.getAddress().getHouseNo());
}
return new ModelAndView("addressForm", "pageDto", pageDto);
}
...it works as desired - sessionDto is NOT filled in with data from the form, so no entry appears in the log (sessionDto.getAddress() == null).
Motivation to have 2 DTOs, one stored in the session:
Any ideas appreciated!
At submit time, the name of the ModelAttribute
has mainly cosmetic effect. The request parameters consist in a hash where keys are the paths of the <form:input>
elements sent by previous response (*), and the values comes from what user typed in the form. But the name of the modelAttribute
is not in the request and Spring has no way to guess it.
It takes all the @ModelAttribute
parameters of the method and tries to set the attributes corresponding the to keys of the request parameters, and when it's done, puts them in the Model
with the name you supplied.
So the name is only used to prepare the response and not to parse the request.
(*) if the request comes from submitting the form, but it could be produced by any other way - simply Spring MVC expects request parameters following that convention