1、模型
- public class Vote {
- private Integer id;
- private String title;
- private Set<VoteItem> voteItems;
- private VoteSubject voteSubject;
- }
- public class VoteItem {
- private Integer id;
- private String content;
- private Vote vote;
- }
2、控制器
- @RequestMapping
- public String vote(@FormModel("votes") Set<Vote> votes) {
- System.out.println(votes);
- return "";
- }
@FormModel注解请参考《扩展SpringMVC以支持更精准的数据绑定1》。
当我们在地址栏输入如:http://localhost:9080/es-web/vote?votes[0].voteItems[0].content=123时,会报:
org.springframework.beans.InvalidPropertyException: Invalid property 'voteItems[0]' of bean class [com.sishuok.es.test.Vote]: Cannot get element with index 0 from Set of size 0, accessed using property path 'voteItems[0]'
3、原因:
原因很明显,Set是无序列表,所以我们使用有顺序注入是不太合适的,BeanWrapperImpl实现:
- else if (value instanceof Set) {
- // Apply index to Iterator in case of a Set.
- Set set = (Set) value;
- int index = Integer.parseInt(key);
- if (index < 0 || index >= set.size()) {
- throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
- "Cannot get element with index " + index + " from Set of size " +
- set.size() + ", accessed using property path '" + propertyName + "'");
- }
- Iterator it = set.iterator();
- for (int j = 0; it.hasNext(); j++) {
- Object elem = it.next();
- if (j == index) {
- value = elem;
- break;
- }
- }
- }
从实现上可以看出,如果set里边有值,那么就能实现绑定。
4、解决方案:
只要保证在springmvc绑定数据之前,给Set里边加上数据即可:
- @ModelAttribute("votes")
- public Set<Vote> initVotes() {
- Set<Vote> votes = new HashSet<Vote>();
- Vote vote = new Vote();
- votes.add(vote);
- vote.setVoteItems(new HashSet<VoteItem>());
- vote.getVoteItems().add(new VoteItem());
- return votes;
- }
这样我们就可以把votes暴露给之前的@FormModel("votes") ,它会使用这个来绑定,所以就有数据了。@ModelAttribute方法请参考《暴露表单引用对象为模型数据》。
但是缺点也很明显,前台必须告诉后台,Set里有几个数据,好让@ModelAttribute方法准备好那么多数据用于数据绑定,比较麻烦。
更简单的解决方案就是使用有序集合,如List,这样最简单。
如上方案通用适用于@ModelAttribute的绑定。
最新的@FormModel的实现请到FormModelMethodArgumentResolver.java下载。