文档章节

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

CanyellWang
 CanyellWang
发布于 2015/11/14 00:11
字数 2352
阅读 2
收藏 0

第一章 Web MVC简介 —— 跟开涛学SpringMVC

第二章 Spring MVC入门 —— 跟开涛学SpringMVC

第三章 DispatcherServlet详解 ——跟开涛学SpringMVC

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

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

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

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

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

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

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

(2PhoneNumber属性编辑器

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

 

java代码:
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代码:
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代码:
<bean name="/dataBind" 
class="cn.javass.chapter4.web.controller.DataBinderTestController"/>

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


java代码:
EL phoneNumber:${dataBinderTest.phoneNumber}<br/>
EL state:${dataBinderTest.state}<br/>
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代码:
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代码:
<!-- 注册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个字符(超长了)那又该怎么处理呢?出错了需要错误消息,那错误消息应该是硬编码?还是可配置呢?

 

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

 

私塾在线学习网原创内容(http://sishuok.com

原创内容,转载请注明私塾在线【http://sishuok.com/forum/blogPost/list/5677.html

本文转载自:http://jinnianshilongnian.iteye.com/blog/1641351

CanyellWang
粉丝 13
博文 155
码字总数 27522
作品 0
海淀
程序员
私信 提问
spring mvc 统一异常处理

1、定义一个统一异常处理类 @ControllerAdvice,是spring3.2提供的新注解, 一般扫描context:component-scan扫描时也能扫描到,不需要在配置文件配置 但如果你的spring-mvc配置文件使用如下方...

qiun
2016/06/24
683
0
springmvc学习笔记(1)-框架原理和入门配置

springmvc学习笔记(1)-框架原理和入门配置 标签: springmvc [TOC] 本文主要介绍springmvc的框架原理,并通过一个入门程序展示环境搭建,配置以及部署调试。 springmvc是spring框架的一个模块...

brianway
2016/03/08
379
0
SpringBoot中的拦截机制

SpringBoot的拦截机制实现不是其特有的,它和Spring中一致,都可以使用下面三种技术来实现拦截机制 过滤器(Filter) 拦截器(Interceptor) 切片(Aspect) 过滤器拦截 过滤器是servlet中的...

Coding小聪
2018/04/06
0
0
【Java】关于Spring MVC框架的总结

SpringMVC是一种基于Java,实现了Web MVC设计模式,请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将Web层进行职责解耦。基于请求驱动指的就是使用请求-响应模型,框架的目的就是...

小铁Winner
2018/07/21
0
0
二 理解DispatcherServlet

要理解DispatcherServlet,首先看一下他的作用,先摘一段开涛的原话: DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,而且负责职责的分派,而且与Spring Io...

Tunie
2014/11/08
198
0

没有更多内容

加载失败,请刷新页面

加载更多

SpringBoot中 集成 redisTemplate 对 Redis 的操作(二)

SpringBoot中 集成 redisTemplate 对 Redis 的操作(二) List 类型的操作 1、 向列表左侧添加数据 Long leftPush = redisTemplate.opsForList().leftPush("name", name); 2、 向列表右......

TcWong
今天
7
0
排序––快速排序(二)

根据排序––快速排序(一)的描述,现准备写一个快速排序的主体框架: 1、首先需要设置一个枢轴元素即setPivot(int i); 2、然后需要与枢轴元素进行比较即int comparePivot(int j); 3、最后...

FAT_mt
昨天
4
0
mysql概览

学习知识,首先要有一个总体的认识。以下为mysql概览 1-架构图 2-Detail csdn |简书 | 头条 | SegmentFault 思否 | 掘金 | 开源中国 |

程序员深夜写bug
昨天
10
0
golang微服务框架go-micro 入门笔记2.2 micro工具之微应用利器micro web

micro web micro 功能非常强大,本文将详细阐述micro web 命令行的功能 阅读本文前你可能需要进行如下知识储备 golang分布式微服务框架go-micro 入门笔记1:搭建go-micro环境, golang微服务框架...

非正式解决方案
昨天
8
0
前端——使用base64编码在页面嵌入图片

因为页面中插入一个图片都要写明图片的路径——相对路径或者绝对路径。而除了具体的网站图片的图片地址,如果是在自己电脑文件夹里的图片,当我们的HTML文件在别人电脑上打开的时候图片则由于...

被毒打的程序猿
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部