Spring Boot中为对象列表布尔字段绑定复选框报错排查
技术栈
- java 8
- spring Boot 2.2.11.RELEASE
- spring 5.2.10.RELEASE
- hibernate 5.4.22.FINAL
- mysql-connector-java 8.0.22
- eclipse oxygen
- straight JSP, no Thymeleaf
问题描述
尝试在表单中渲染对象列表,其中对象包含boolean类型字段,需要与对应的复选框绑定。目前其他字段均正常工作,仅该布尔字段绑定复选框时出现问题。
实体类代码
public class PhoneNumberByAreaCodeSelected extends PhoneNumberByAreaCode implements java.io.Serializable { private static final long serialVersionUID = 3628007212507237450L; private boolean selected; //constructors public PhoneNumberByAreaCodeSelected() {} public PhoneNumberByAreaCodeSelected( String areaCode, String state, BigInteger total, boolean selected) { super(areaCode, state, total); this.selected = selected; } public PhoneNumberByAreaCodeSelected( PhoneNumberByAreaCode obj) { super.setAreaCode( obj.getAreaCode()); super.setState( obj.getState()); super.setTotal(obj.getTotal()); this.selected = false; } //getters and setters public boolean isSelected() { return selected; } public boolean getSelected() { return selected; } public void setSelected(boolean selected) { this.selected = selected; } }
表单后台Bean代码
public class PhoneNumberByAreaCodeSelectedList { private List<PhoneNumberByAreaCodeSelected> lisst; //constructors public PhoneNumberByAreaCodeSelectedList() { this.lisst = new ArrayList<PhoneNumberByAreaCodeSelected>(); } public PhoneNumberByAreaCodeSelectedList( List<PhoneNumberByAreaCodeSelected> lisst) { this.lisst = lisst; } //getters and setters public List<PhoneNumberByAreaCodeSelected> getLisst() { return lisst; } public void setLisst(List<PhoneNumberByAreaCodeSelected> lisst) { this.lisst = lisst; } }
控制器代码
@RequestMapping(value="/list_phone_numbers_by_area_code", method = RequestMethod.GET) @ModelAttribute("phoneNumberByAreaCodeSelectedList") public ModelAndView showListPhoneNumbersForm( ) { List<PhoneNumberByAreaCode> lisst = service.groupPhoneNumbersByStateAndAreaCode(); List<PhoneNumberByAreaCodeSelected> lisst_selected = new LinkedList<>(); ListIterator<PhoneNumberByAreaCode> li = lisst.listIterator(); while( li.hasNext()) { lisst_selected.add( new PhoneNumberByAreaCodeSelected( li.next())); } PhoneNumberByAreaCodeSelectedList obj = new PhoneNumberByAreaCodeSelectedList(); obj.setLisst(lisst_selected); ModelAndView mv = new ModelAndView(); mv.setViewName("list_phone_numbers_by_area_code"); mv.addObject("cnt", obj.getLisst().size()); mv.addObject("phoneNumberByAreaCodeSelectedList", obj); mv.addObject("timestamp", Utilities.getCurrentTimestamp()); return mv; } @RequestMapping(value="/list_phone_numbers_by_area_code", method = RequestMethod.POST) @ModelAttribute("phoneNumberByAreaCodeSelectedList") public ModelAndView handleListPhoneNumbersForm( PhoneNumberByAreaCodeSelectedList obj, BindingResult result) { List<PhoneNumberByAreaCodeSelected> lisst = obj.getLisst(); ModelAndView mv = new ModelAndView(); mv.setViewName("create_campaign"); mv.addObject("cnt", lisst.size()); mv.addObject("groups", lisst ); mv.addObject("timestamp", Utilities.getCurrentTimestamp()); return mv; }
原正常显示的JSP代码
<body> ${cnt} elements in lisst on server side <br> <form:form modelAttribute="phoneNumberByAreaCodeSelectedList" method="post"> <div class="container"> <table> <thead> <tr> <th> </th> <th>areaCode</th> <th>state</th> <th>total</th> </tr> </thead> <tbody> <c:forEach items="${phoneNumberByAreaCodeSelectedList.lisst}" var="item" varStatus="loop"> <tr> <td>${item.areaCode}</td> <td>${item.state}</td> <td>${item.total}</td> </tr> </c:forEach> </tbody> </table> <br> <input type="reset" value="Reset" /> <input type="submit" value="Cancel" /> <input type="submit" value="Submit"/> </div> </form:form> <br> <nav class="navbar navbar-default navbar-fixed-bottom"> <div class="container-fluid"> <div align="center">generated ${timestamp}</div> </div> </nav> </body>
添加复选框后报错的JSP代码
<body> ${cnt} elements in lisst on server side <br> <form:form modelAttribute="phoneNumberByAreaCodeSelectedList" method="post"> <div class="container"> <table> <thead> <tr> <th> </th> <th>areaCode</th> <th>state</th> <th>total</th> </tr> </thead> <tbody> <c:forEach items="${phoneNumberByAreaCodeSelectedList.lisst}" var="item" varStatus="loop"> <tr> <td><form:checkbox path="items[${loop.index}].selected" name="selected" value="${item.selected}"/></td> <!-- added --> <td>${item.areaCode}</td> <td>${item.state}</td> <td>${item.total}</td> </tr> </c:forEach> </tbody> </table> <br> <input type="reset" value="Reset" /> <input type="submit" value="Cancel" /> <input type="submit" value="Submit"/> </div> </form:form> <br> <nav class="navbar navbar-default navbar-fixed-bottom"> <div class="container-fluid"> <div align="center">generated ${timestamp}</div> </div> </nav> </body>
报错信息
Caused by: org.springframework.beans.NotReadablePropertyException: Invalid property 'items[0]' of bean class [com.faxcount.application.model.PhoneNumberByAreaCodeSelectedList]: Bean property 'items[0]' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
解决办法
报错原因很明确:你在form:checkbox的path属性中使用了items[${loop.index}].selected,但你的表单BeanPhoneNumberByAreaCodeSelectedList中存储列表的字段是lisst,而非items,Spring无法找到对应的getItems()方法,因此抛出属性不可读异常。
修改JSP中的复选框代码,将path改为lisst[${loop.index}].selected,并且不需要手动指定name和value属性,Spring表单标签会自动处理这些属性并完成绑定:
<td><form:checkbox path="lisst[${loop.index}].selected"/></td>
修改后的完整JSP代码如下:
<body> ${cnt} elements in lisst on server side <br> <form:form modelAttribute="phoneNumberByAreaCodeSelectedList" method="post"> <div class="container"> <table> <thead> <tr> <th> </th> <th>areaCode</th> <th>state</th> <th>total</th> </tr> </thead> <tbody> <c:forEach items="${phoneNumberByAreaCodeSelectedList.lisst}" var="item" varStatus="loop"> <tr> <td><form:checkbox path="lisst[${loop.index}].selected"/></td> <td>${item.areaCode}</td> <td>${item.state}</td> <td>${item.total}</td> </tr> </c:forEach> </tbody> </table> <br> <input type="reset" value="Reset" /> <input type="submit" value="Cancel" /> <input type="submit" value="Submit"/> </div> </form:form> <br> <nav class="navbar navbar-default navbar-fixed-bottom"> <div class="container-fluid"> <div align="center">generated ${timestamp}</div> </div> </nav> </body>
这样修改后,Spring就能正确识别列表字段,完成复选框与selected布尔字段的绑定,提交表单后也能正确获取选中状态。
内容的提问来源于stack exchange,提问作者code_warrior

