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<——>Boolean

true/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代码:

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

(2、PhoneNumber属性编辑器

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

java代码:

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

PropertyEditorSupport:一个PropertyEditor的支持类;

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

getAsText:表示将PhoneNumberModel——>String。

(3、控制器

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

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

java代码:

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

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

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

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

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

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

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

java代码:

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

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

java代码:

Java代码 
  1. EL phoneNumber:${dataBinderTest.phoneNumber}<br/>
  2. EL state:${dataBinderTest.state}<br/>
  3. EL date:${dataBinderTest.date}<br/>

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

(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代码:

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

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

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

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

java代码:

Java代码 
  1. <!-- 注册WebBindingInitializer实现 -->
  2. <bean id="myWebBindingInitializer"class="cn.javass.chapter4.web.controller.support.initializer.MyWebBindingInitializer"/>
  3. <bean name="/dataBind"class="cn.javass.chapter4.web.controller.DataBinderTestController">
  4. <!-- 注入WebBindingInitializer实现 -->
  5. <property name="webBindingInitializer" ref="myWebBindingInitializer"/>
  6. </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个字符(超长了)那又该怎么处理呢?出错了需要错误消息,那错误消息应该是硬编码?还是可配置呢?

Controller接口控制器(6)相关推荐

  1. 第四章 Controller接口控制器详解(5)——跟着开涛学SpringMVC

    2019独角兽企业重金招聘Python工程师标准>>> 原创内容,转载请注明iteye http://jinnianshilongnian.iteye.com/ 4.15.Multi ...

  2. Controller接口控制器(7)

    4.16.2.数据验证 1.数据绑定失败:比如需要数字却输入了字母: 2.数据不合法:可以认为是业务错误,通过自定义验证器验证,如用户名长度必须在5-20之间,我们却输入了100个字符等: 3.错误对 ...

  3. Controller接口控制器(5)

    4.15.MultiActionController 之前学过的控制器如AbstractCommandController.SimpleFormController等一般对应一个功能处理方法(如新增) ...

  4. Controller接口控制器详解(1)

    4.1.Controller简介 Controller控制器,是MVC中的部分C,为什么是部分呢?因为此处的控制器主要负责功能处理部分: 1.收集.验证请求参数并绑定到命令对象: 2.将命令对象交给业 ...

  5. (三)Controller接口控制器详解(二)

    一.AbstractController(简单控制器) AbstractController使用方法: 首先让我们使用AbstractController来重写第二章的HelloWorldContro ...

  6. Controller接口控制器(3)

    4.11.AbstractWizardFormController 向导控制器类提供了多步骤(向导)表单的支持(如完善个人资料时分步骤填写基本信息.工作信息.学校信息等) 假设现在做一个完善个人信息的 ...

  7. Controller接口控制器(2)

    4.5.ServletForwardingController 将接收到的请求转发到一个命名的servlet,具体示例如下: java代码: Java代码  package cn.javass.cha ...

  8. Controller接口控制器(4)

    4.12.ParameterizableViewController 参数化视图控制器,不进行功能处理(即静态视图),根据参数的逻辑视图名直接选择需要展示的视图. Java代码  <bean n ...

  9. 实现接口Controller定义控制器

    实现接口Controller定义控制器 控制器提供访问应用程序的行为,通常通过服务接口定义或注解定义两种方法实现. 控制器解析用户的请求并将其转换为一个模型.在Spring MVC中一个控制器可以包含 ...

最新文章

  1. Compute API 关键概念 详解
  2. Windows下使用apache模块实现合并多个js、css提高网页加载速度
  3. Windows XP硬盘安装Ubuntu 12.04双系统
  4. 【BZOJ3518】点组计数 [欧拉函数]
  5. linux云服务终端提示符显示-bash-4.2#解决方法
  6. C/C++遍历进程和进程ID的小工具
  7. 图论 —— 图的遍历
  8. HashMap Hashtable区别
  9. SQLi LABS Less-39
  10. css sprite 介绍和实例
  11. Android Application基本组成部分
  12. JavaWeb初级篇-HttpPost使用教程
  13. 360极速浏览器兼容模式怎么设置在哪
  14. MovieLens推荐系统数据集官方文档+解释
  15. Filter过滤器详解
  16. CVPR2020论文列表(中英对照)
  17. BottomNavigationView 动态切换Icon且保留原图颜色
  18. IT 如何把骨干留住
  19. 世界顶级黑客,能入侵一台没有联网的电脑吗?
  20. 趋势顶底指标公式 通达信趋势顶底主图选股指标 清晰的趋势顶底详解

热门文章

  1. ac86u原厂固件去广告_苹果发布最新固件IOS12.3.2,估计不修正你的手机
  2. java mqtt丢包_MQTT 3.1协议非严肃反思录
  3. html 判断当前窗口是否是子窗口,JavaScript window.open 判断子窗口是否已经存在
  4. android 平板怎么截图,平板电脑怎么截图 平板电脑截图方法【详解】
  5. 使用可自定义的定期计划自动执行数据库备份
  6. 使用EF Core和AngularJS的Master Chef(第3部分)ASP.NET Core MVC
  7. LINQ to CSV,一种类型安全,动态的高性能方法
  8. python可哈希_Python,TypeError:不可哈希类型:'list'
  9. java流被关闭后怎样重新打开,java – 如何停止MediaPlayer流然后重新启动它? Android的...
  10. java数组移除对象_如何从Java数组中删除对象?