Spring接口类型数据绑定列表 - 如何?

时间:2012-01-22 20:47:17

标签: forms spring data-binding binding spring-mvc

试图在网上找到答案但却失败了。对于专业的Spring Devs来说应该很简单......所以它来了:

简单地说,我想将接口类型List:List绑定到表单并获取数据(可能由用户通过表单修改。问题是它不起作用:(

我的代码(简短版本) - 传递给表单的命令/模型类:

public class RoomsFormSearchResultCommand extends RoomsFormSearchCommand {
    @SuppressWarnings("unchecked")
    private List<IRoom> roomsList = LazyList.decorate(new ArrayList<Room>(),
            FactoryUtils.instantiateFactory(Room.class));

    public List<IRoom> getRoomsList() {
        return roomsList;
    }

    public void setRoomsList(final List<IRoom> roomsList) {
        this.roomsList = roomsList;
    }

(...)

然后在我使用它的形式(短版):

    <form:form method="post" action="reserve" commandName="roomsResultsCmd">
(...)
    <c:forEach var="room" items="${roomsResultsCmd.roomsList}"
                varStatus="status">
                <tr>
                    <td><form:input path="roomsList[${status.index}].roomNumber" readonly="true"/>
(...)

表单显示正常但提交后我得到:

2012-01-22 21:31:55 org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [wyspa] in context with path [/wyspa] threw exception [Request processing failed; nested exception is org.springframework.beans.InvalidPropertyException: Invalid property 'roomsList[0]' of bean class [com.wyspa.controller.command.RoomsFormSearchResultCommand]: Illegal attempt to get property 'roomsList' threw exception; nested exception is org.springframework.beans.NullValueInNestedPathException: Invalid property 'roomsList' of bean class [com.wyspa.controller.command.RoomsFormSearchResultCommand]: Could not instantiate property type [com.wyspa.entity.IRoom] to auto-grow nested property path: java.lang.InstantiationException: com.wyspa.entity.IRoom] with root cause
org.springframework.beans.NullValueInNestedPathException: Invalid property 'roomsList' of bean class [com.wyspa.controller.command.RoomsFormSearchResultCommand]: Could not instantiate property type [com.wyspa.entity.IRoom] to auto-grow nested property path: java.lang.InstantiationException: com.wyspa.entity.IRoom
    at org.springframework.beans.BeanWrapperImpl.newValue(BeanWrapperImpl.java:633)
    at org.springframework.beans.BeanWrapperImpl.growCollectionIfNecessary(BeanWrapperImpl.java:863)
    at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:770)
    at org.springframework.beans.BeanWrapperImpl.getNestedBeanWrapper(BeanWrapperImpl.java:555)

(...)

当我将List更改为“实例”列表时,一切正常!

public class RoomsFormSearchResultCommand extends RoomsFormSearchCommand {
    @SuppressWarnings("unchecked")
//notice that the List is now List<Room>
    private List<Room> roomsList = LazyList.decorate(new ArrayList<Room>(),
            FactoryUtils.instantiateFactory(Room.class));

在这种情况下,数据以适当的方式传递给控制器​​。

由于我习惯于开发接口而我非常疯狂,我真的不愿意将List<IRoom>(从服务返回)转换为List<Room>,这似乎适合Spring。在这种情况下是否可以使用List<IRoom>或Spring只是不支持它?

//当然Room实现了IRoom - 但我想你已经有了......

我会非常高兴获得任何帮助/建议!

最诚挚的问候, Nirwan

3 个答案:

答案 0 :(得分:3)

我有完全相同的问题。更改为以下将无法解决问题。看起来spring绑定会忽略工厂的utils并尝试实例化null对象本身:

@SuppressWarnings("unchecked")
private List<IRoom> roomsList = LazyList.decorate(new ArrayList<IRoom>(),
    FactoryUtils.instantiateFactory(Room.class));

解决方法是在控制器中设置自动增长嵌套路径:

@InitBinder protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
    binder.setAutoGrowNestedPaths(false);
    super.initBinder(request, binder);
 }

问题是你将失去像user.account.address.street这样方便的嵌套路径。您必须确保用户,帐户,地址都不为空。它确实会导致很多问题。这就是我来到这里的原因,看看我是否能找到更好的解决方案。

答案 1 :(得分:1)

如果您实际上不需要列表自动增长,则可以将表单对象存储在会话中,以避免禁用自动增长嵌套路径的令人讨厌的副作用。

@Controller
@SessionAttributes(types = RoomsFormSearchResultCommand.class)
public final class SearchController {

    @InitBinder
    protected void initBinder(final WebDataBinder binder) {
            binder.setAutoGrowNestedPaths(false);
    }

    @RequestMapping(method = RequestMethod.GET)
    public String showForm(final Model model) {
            RoomsFormSearchResultCommand form = ... // create or load form
            model.addAttribute(form); 
    }

    @RequestMapping(method = RequestMethod.POST)
    public String onSubmitUpdateCart(
                    @ModelAttribute final RoomsFormSearchResultCommand form,
                    final BindingResult result,
                    final SessionStatus status) {
            // if result has no errors, just set status to complete
            status.setComplete();
    }
}

答案 2 :(得分:0)

请尝试以下行

@SuppressWarnings("unchecked")
private List<IRoom> roomsList = LazyList.decorate(new ArrayList<IRoom>(),
    FactoryUtils.instantiateFactory(Room.class));

没有时间自己尝试,但这是有道理的。