跟开涛学SpringMVC(4.6):Controller接口控制器详解(6)

4.16、数据类型转换和数据验证

流程:

1、首先创建数据绑定器,在此此会创建ServletRequestDataBinder类的对象,并设置messageCodesResolver(错误码解析器);

2、提供第一个扩展点,初始化数据绑定器,在此处我们可以覆盖该方法注册自定义的PropertyEditor(请求参数——>命令对象属性的转换);

3、进行数据绑定,即请求参数——>命令对象的绑定;

4、提供第二个扩展点,数据绑定完成后的扩展点,此处可以实现一些自定义的绑定动作;

5、验证器对象的验证,验证器通过validators注入,如果验证失败,需要把错误信息放入Errors(此处使用BindException实现);

6、提供第三个扩展点,此处可以实现自定义的绑定/验证逻辑;

7、将errors传入功能处理方法进行处理,功能方法应该判断该错误对象是否有错误进行相应的处理。

4.16.1、数据类型转换

请求参数(String)——>命令对象属性(可能是任意类型)的类型转换,即数据绑定时的类型转换,使用PropertyEditor实现绑定时的类型转换。

一、spring内建的PropertyEditor如下所示:

类名 说明 默认是否注册
ByteArrayPropertyEditor String<——>byte[]
ClassEditor String<——>Class当类没有发现抛出IllegalArgumentException
CustomBooleanEditor String<——>Booleantrue/yes/on/1转换为true,false/no/off/0转换为false
CustomCollectionEditor 数组/Collection——>Collection普通值——>Collection(只包含一个对象)如String——>Collection不允许Collection——>String(单方向转换)
CustomNumberEditor String<——>Number(Integer、Long、Double)
FileEditor String<——>File
InputStreamEditor String——>InputStream单向的,不能InputStream——>String
LocaleEditor String<——>Locale,(String的形式为[语言]_[国家]_[变量],这与Local对象的toString()方法得到的结果相同)
PatternEditor String<——>Pattern
PropertiesEditor String<——>Java.lang.Properties
URLEditor String<——>URL
StringTrimmerEditor 一个用于trim 的 String类型的属性编辑器如默认删除两边的空格,charsToDelete属性:可以设置为其他字符emptyAsNull属性:将一个空字符串转化为null值的选项。 ×
CustomDateEditor String<——>java.util.Date ×

二、Spring内建的PropertyEditor支持的属性(符合JavaBean规范)操作:

表达式 设值/取值说明
username 属性username设值方法setUsername()/取值方法getUsername() 或 isUsername()
schooInfo.schoolType 属性schooInfo的嵌套属性schoolType设值方法getSchooInfo().setSchoolType()/取值方法getSchooInfo().getSchoolType()
hobbyList[0] 属性hobbyList的第一个元素索引属性可能是一个数组、列表、其它天然有序的容器。
map[key] 属性map(java.util.Map类型)map中key对应的值

三、示例:

接下来我们写自定义的属性编辑器进行数据绑定:

(1、模型对象:

java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package cn.javass.chapter4.model;
//省略import
public class DataBinderTestModel {
     private String username;
     private boolean bool; //Boolean值测试
     private SchoolInfoModel schooInfo;
     private List hobbyList; //集合测试,此处可以改为数组/Set进行测试
     private Map map; //Map测试
     private PhoneNumberModel phoneNumber; //String->自定义对象的转换测试
     private Date date; //日期类型测试
     private UserState state; //String——>Enum类型转换测试
     //省略getter/setter
}
 
package cn.javass.chapter4.model;
//如格式010-12345678
public class PhoneNumberModel {
     private String areaCode; //区号
     private String phoneNumber; //电话号码
     //省略getter/setter
}

(2、PhoneNumber属性编辑器

前台输入如010-12345678自动转换为PhoneNumberModel。

java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package cn.javass.chapter4.web.controller.support.editor;
//省略import
public class PhoneNumberEditor extends PropertyEditorSupport {
     Pattern pattern = Pattern.compile( "^(\\d{3,4})-(\\d{7,8})$" );
     @Override
     public void setAsText(String text) throws IllegalArgumentException {
         if (text == null || !StringUtils.hasLength(text)) {
             setValue( null ); //如果没值,设值为null
         }
         Matcher matcher = pattern.matcher(text);
         if (matcher.matches()) {
             PhoneNumberModel phoneNumber = new PhoneNumberModel();
             phoneNumber.setAreaCode(matcher.group( 1 ));
             phoneNumber.setPhoneNumber(matcher.group( 2 ));
             setValue(phoneNumber);
         } else {
             throw new IllegalArgumentException(String.format( "类型转换失败,需要格式[010-12345678],但格式是[%s]" , text));
         }
     }
     @Override
     public String getAsText() {
         PhoneNumberModel phoneNumber = ((PhoneNumberModel)getValue());
         return phoneNumber == null ? "" : phoneNumber.getAreaCode() + "-" + phoneNumber.getPhoneNumber();
     }
}

PropertyEditorSupport:一个PropertyEditor的支持类;

setAsText:表示将String——>PhoneNumberModel,根据正则表达式进行转换,如果转换失败抛出异常,则接下来的验证器会进行验证处理;

getAsText:表示将PhoneNumberModel——>String。

(3、控制器

需要在控制器注册我们自定义的属性编辑器。

此处我们使用AbstractCommandController,因为它继承了BaseCommandController,拥有绑定流程。

java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package cn.javass.chapter4.web.controller;
//省略import
public class DataBinderTestController extends AbstractCommandController {
     public DataBinderTestController() {
         setCommandClass(DataBinderTestModel. class ); //设置命令对象
         setCommandName( "dataBinderTest" ); //设置命令对象的名字
     }
     @Override
     protected ModelAndView handle(HttpServletRequest req, HttpServletResponse resp, Object command, BindException errors) throws Exception {
         //输出command对象看看是否绑定正确
         System.out.println(command);
         return new ModelAndView( "bindAndValidate/success" ).addObject( "dataBinderTest" , command);
     }
     @Override
     protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
         super .initBinder(request, binder);
         //注册自定义的属性编辑器
         //1、日期
         DateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
         CustomDateEditor dateEditor = new CustomDateEditor(df, true );
         //表示如果命令对象有Date类型的属性,将使用该属性编辑器进行类型转换
         binder.registerCustomEditor(Date. class , dateEditor);
         //自定义的电话号码编辑器
         binder.registerCustomEditor(PhoneNumberModel. class , new PhoneNumberEditor());
     }
}

initBinder:第一个扩展点,初始化数据绑定器,在此处我们注册了两个属性编辑器;

CustomDateEditor:自定义的日期编辑器,用于在String<——>日期之间转换;

binder.registerCustomEditor(Date.class, dateEditor):表示如果命令对象是Date类型,则使用dateEditor进行类型转换;

PhoneNumberEditor:自定义的电话号码属性编辑器用于在String<——> PhoneNumberModel之间转换;

binder.registerCustomEditor(PhoneNumberModel.classnewPhoneNumberEditor()):表示如果命令对象是PhoneNumberModel类型,则使用PhoneNumberEditor进行类型转换;

(4、spring配置文件chapter4-servlet.xml

java代码:

1
2
<bean name= "/dataBind"
class = "cn.javass.chapter4.web.controller.DataBinderTestController" />

(5、视图页面(WEB-INF/jsp/bindAndValidate/success.jsp)

java代码:

1
2
3
EL phoneNumber:${dataBinderTest.phoneNumber}
EL state:${dataBinderTest.state}
EL date:${dataBinderTest.date}

视图页面的数据没有预期被格式化,如何进行格式化显示呢?请参考【第七章  注解式控制器的数据验证、类型转换及格式化】。

(6、测试:

1、在浏览器地址栏输入请求的URL,如

http://localhost:9080/springmvc-chapter4/dataBind?username=zhang&bool=yes&schooInfo.specialty=computer&hobbyList[0]=program&hobbyList[1]=music&map[key1]=value1&map[key2]=value2&phoneNumber=010-12345678&date=2012-3-18 16:48:48&state=blocked

2、控制器输出的内容:

DataBinderTestModel [username=zhang, bool=true, schooInfo=SchoolInfoModel [schoolType=null, schoolName=null, specialty=computer], hobbyList=[program, music], map={key1=value1, key2=value2}, phoneNumber=PhoneNumberModel [areaCode=010, phoneNumber=12345678], date=Sun Mar 18 16:48:48 CST 2012, state=锁定]

类型转换如图所示:

四、注册PropertyEditor

1、使用WebDataBinder进行控制器级别注册PropertyEditor(控制器独享)

如“【三、示例】”中所使用的方式,使用WebDataBinder注册控制器级别的PropertyEditor,这种方式注册的PropertyEditor只对当前控制器独享,即其他的控制器不会自动注册这个PropertyEditor,如果需要还需要再注册一下。

2、使用WebBindingInitializer批量注册PropertyEditor

如果想在多个控制器同时注册多个相同的PropertyEditor时,可以考虑使用WebBindingInitializer。

示例:

(1、实现WebBindingInitializer

java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package cn.javass.chapter4.web.controller.support.initializer;
//省略import
public class MyWebBindingInitializer implements WebBindingInitializer {
     @Override
     public void initBinder(WebDataBinder binder, WebRequest request) {
         //注册自定义的属性编辑器
         //1、日期
         DateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
         CustomDateEditor dateEditor = new CustomDateEditor(df, true );
         //表示如果命令对象有Date类型的属性,将使用该属性编辑器进行类型转换
         binder.registerCustomEditor(Date. class , dateEditor);
         //自定义的电话号码编辑器
         binder.registerCustomEditor(PhoneNumberModel. class , new PhoneNumberEditor());
     }
}

通过实现WebBindingInitializer并通过binder注册多个PropertyEditor。

(2、修改【三、示例】中的DataBinderTestController,注释掉initBinder方法;

(3、修改chapter4-servlet.xml配置文件:

java代码:

1
2
3
4
5
6
<!-- 注册WebBindingInitializer实现 -->
<bean id= "myWebBindingInitializer" class = "cn.javass.chapter4.web.controller.support.initializer.MyWebBindingInitializer" />
<bean name= "/dataBind" class = "cn.javass.chapter4.web.controller.DataBinderTestController" >
     <!-- 注入WebBindingInitializer实现 -->
     <property name= "webBindingInitializer" ref= "myWebBindingInitializer" />
</bean>

(4、尝试访问“【三、示例】”中的测试URL即可成功。

使用WebBindingInitializer的好处是当你需要在多个控制器中需要同时使用多个相同的PropertyEditor可以在WebBindingInitializer实现中注册,这样只需要在控制器中注入WebBindingInitializer即可注入多个PropertyEditor。

3、全局级别注册PropertyEditor(全局共享)

只需要将我们自定义的PropertyEditor放在和你的模型类同包下即可,且你的Editor命名规则必须是“模型类名Editor”,这样Spring会自动使用标准JavaBean架构进行自动识别,如图所示:

此时我们把“DataBinderTestController”的“binder.registerCustomEditor(PhoneNumberModel.class, new PhoneNumberEditor());”注释掉,再尝试访问“【三、示例】”中的测试URL即可成功。

这种方式不仅仅在使用Spring时可用,在标准的JavaBean等环境都是可用的,可以认为是全局共享的(不仅仅是Spring环境)。

PropertyEditor被限制为只能String<——>Object之间转换,不能Object<——>Object,Spring3提供了更强大的类型转换(TypeConversion)支持,它可以在任意对象之间进行类型转换,不仅仅是String<——>Object。

如果我在地址栏输入错误的数据,即数据绑定失败,Spring Web MVC该如何处理呢?如果我输入的数据不合法呢?如用户名输入100个字符(超长了)那又该怎么处理呢?出错了需要错误消息,那错误消息应该是硬编码?还是可配置呢?

接下来我们来学习一下数据验证器进行数据验证吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值