- model對象:User.java:
- public class User {
- private int id;
- private String name;
- //0:男,1:女,頁面上表現為radiobutton
- private int gender;
- private int age;
- //0:沒畢業,1:已經畢業,頁面上表現為checkbox
- private int graduted;
- //0:沒結婚,1:已經結婚,頁面上表現為checkbox
- private int married;
- }
form表單:
<form:form id="user" modelAttribute="user" action="save.do">
<table>
<tr><td>用戶名:</td><td><form:input path="name" /></td></tr>
<tr><td>性別:</td><td>男:<form:radiobutton path="gender" value="0"/>
女:<form:radiobutton path="gender" value="1"/></td>
</tr>
<tr><td>年齡:</td><td><form:input path="age" /></td></tr>
<!-- Attribute 'value' is required when binding to non-boolean values -->
<tr><td>其他:</td><td>畢業:<form:checkbox path="graduted" value="1"/>婚否:<form:checkbox path="married" value="1"/></td>
</tr>
<tr><td colspan="2"><input type="submit" value="提交"></td></tr>
</table>
</form:form>
- UserController:
- @Controller
- @RequestMapping("/user")
- public class UserController{
- private static class IntEditor extends CustomNumberEditor{
- public IntEditor(){
- super(Integer.class,true);
- }
- public void setAsText(String text) throws IllegalArgumentException {
- if (text==null || text.trim().equals("")) {
- // Treat empty String as null value.
- setValue(0);
- } else {
- // Use default valueOf methods for parsing text.
- setValue(Integer.parseInt(text));
- }
- }
- }
- @InitBinder
- public void initBinder(WebDataBinder binder){
- binder.registerCustomEditor(int.class, null, new IntEditor());
- }
- @RequestMapping(value="/add")
- public ModelAndView add(HttpServletRequest request){
- ModelAndView mav = new ModelAndView("useradd");
- //Neither BindingResult nor plain target object for bean name 'user' available as request attribute
- mav.addObject("user", new User());
- return mav;
- }
- @RequestMapping(value="/save")
- public ModelAndView save(HttpServletRequest request,
- @ModelAttribute("user") User user){
- ModelAndView mav = new ModelAndView("redirect:list.do");
- System.out.println(user);
- return mav;
- }
- }
瀏覽器輸入:http://localhost:8080/sp3form/user/add.do,會彈出輸入頁面,如果,兩個checkbox都不選中,然后,提交的時候,會報錯:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errors
Field error in object 'user' on field 'graduted': rejected value [null]; codes [typeMismatch.user.graduted,typeMismatch.graduted,typeMismatch.int,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.graduted,graduted]; arguments []; default message [graduted]]; default message [Failed to convert property value of type 'null' to required type 'int' for property 'graduted'; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [null] to required type [int] for property 'graduted': PropertyEditor [org.springframework.beans.propertyeditors.CustomNumberEditor] returned inappropriate value]
Field error in object 'user' on field 'married': rejected value [null]; codes [typeMismatch.user.married,typeMismatch.married,typeMismatch.int,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.married,married]; arguments []; default message [married]]; default message [Failed to convert property value of type 'null' to required type 'int' for property 'married'; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [null] to required type [int] for property 'married': PropertyEditor [org.springframework.beans.propertyeditors.CustomNumberEditor] returned inappropriate value]
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:863)
具體的來說就是:null無法轉化成int。
然后,上網一通google,基本上是兩種解決辦法:
(1)使用Integer,而不是int,null不能轉化成int,但是可以轉化成為Integer。這么做轉化雖然沒問題,但是,如果把model對象插入到數據庫的時候還得把null改成0,麻煩!
(2)添加一個InitBinder,重寫setAsText()方法,如同上面的代碼那樣,可仍然報錯!一開始懷疑是setAsText方法沒被調用,但是debug的時候發現,有兩個值確實調用了setAsText,但是checkbox卻並沒有調用。也就是說,在setAsText之前就已經出錯了!
大家都是你抄我來我抄你,沒有一個能解決問題,所謂靠牆牆倒,靠人人跑,本來想偷點懶的,關鍵時候還得靠自己啊!
然后,就開始debug,看看源碼里面是如何處理的。最開始找到的解決辦法是重寫WebDataBinder里面的getEmptyValue方法:
- protected Object getEmptyValue(String field, Class fieldType) {
- if (fieldType != null && boolean.class.equals(fieldType) || Boolean.class.equals(fieldType)) {
- // Special handling of boolean property.
- return Boolean.FALSE;
- }
- else if (fieldType != null && fieldType.isArray()) {
- // Special handling of array property.
- return Array.newInstance(fieldType.getComponentType(), 0);
- }else if(fieldType == int.class || fieldType == long.class){
- return 0;
- }
- else {
- // Default value: try null.
- return null;
- }
- }
這樣從源頭上,就把問題給解決了,但是,這還要修改spring的源代碼,貌似不太理想。
繼續debug:
TypeConverterDelegate:
- /**
- * Convert the given text value using the given property editor.
- * @param oldValue the previous value, if available (may be <code>null</code>)
- * @param newTextValue the proposed text value
- * @param editor the PropertyEditor to use
- * @return the converted value
- */
- protected Object doConvertTextValue(Object oldValue, String newTextValue, PropertyEditor editor) {
- try {
- editor.setValue(oldValue);
- }
- catch (Exception ex) {
- if (logger.isDebugEnabled()) {
- logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
- }
- // Swallow and proceed.
- }
- editor.setAsText(newTextValue);
- return editor.getValue();
- }
到這里才發現,spring首先調用的是自定義的editor的setValue然后才調用的setAsText,因此:
- public void setAsText(String text) throws IllegalArgumentException {
- if (text==null || text.trim().equals("")) {
- // Treat empty String as null value.
- setValue(0);
- } else {
- // Use default valueOf methods for parsing text.
- setValue(Integer.parseInt(text));
- }
- }
這種方式根本就是行不通的!
既然找到了原因,那么,也就找到了解決辦法,我們需要重寫setValue,而不是setAsText:
- private static class IntEditor extends CustomNumberEditor{
- public IntEditor(){
- super(Integer.class,true);
- }
- @Override
- public void setValue(Object value) {
- if(value == null){
- super.setValue(0);
- }else{
- super.setValue(value);
- }
- }
- }
- @InitBinder
- public void initBinder(WebDataBinder binder){
- binder.registerCustomEditor(int.class, "graduted", new IntEditor());
- binder.registerCustomEditor(int.class, "married", new IntEditor());
- }
世界從此清凈了!
