【问题标题】:Spring - @ModelAttribute behaviourSpring - @ModelAttribute 行为
【发布时间】:2014-12-20 04:11:11
【问题描述】:

我在使用 @ModelAttribute 时遇到问题。

拥有一个具有这种上下文的简单 Spring (3.2.11) mvc 应用程序:

<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> 

有2个DTO:PageDTO和SessionDTO,这两个DTO里面完全一样,只包含一个属性——Address地址和get/set方法。

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
}

拥有一个带有一个表单的 addressForm.jsp:

<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>

拥有一个控制器:

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);
    }

}

问题来了。当我提交地址表格时,两个 DTO 都填写了表格中的数据。我希望只填写jsp中form标签中提到的“pageDto”名称的模型属性。

来自表单提交事件的日志条目:

[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

当我像这样更改控制器中的 processForm 方法时:

@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);
}

...它按需要工作 - sessionDto 没有填写表单中的数据,因此日志中不会出现任何条目(sessionDto.getAddress() == null)。

拥有 2 个 DTO 的动机,其中一个存储在会话中:

  • 并非模型的所有属性都显示在表单上,​​所以我必须使用 input type=hidden 不要丢失数据
  • 我需要能够找出用户在提交表单时更改了哪些属性

任何想法表示赞赏!

【问题讨论】:

  • 自己将其存储在会话中并检索它。不要将其用作@ModelAttribute,因为它确实不是那样的。问题是数据绑定只是根据名称将请求参数绑定到对象。因此,如果有一个参数houseNo 并且在任何模型对象上有一个setHouseNo,它将被绑定。
  • 如果不区分模型对象,那么@ModelAttribute("name")中的名字是什么意思呢?
  • 从模型中检索正确的对象,但考虑绑定所有模型对象。

标签: java spring spring-mvc modelattribute


【解决方案1】:

在提交时,ModelAttribute 的名称主要具有修饰效果。请求参数包含在 hash 中,其中键是先前响应 (*) 发送的 &lt;form:input&gt; 元素的路径,值来自用户在表单中键入的内容。但是modelAttribute的名字在请求中是不是的,Spring没法猜到。

它获取方法的所有@ModelAttribute参数,并尝试将对应的属性设置为请求参数的keys,完成后放入Model使用您提供的名称

所以这个名字只是用来准备响应而不是解析请求。

(*) 如果请求来自提交表单,但它可以通过任何其他方式生成 - 只是 Spring MVC 期望请求参数遵循该约定

【讨论】:

  • 谢谢,我明白了。
猜你喜欢
  • 2013-04-14
  • 1970-01-01
  • 1970-01-01
  • 2017-02-23
  • 1970-01-01
  • 2018-08-07
  • 2012-08-25
  • 2014-09-20
  • 2021-12-14
相关资源
最近更新 更多