Spring MVC 类型转换与格式化
本小记学习目标
-
类型转换和格式化的介绍
-
Converter的使用介绍
-
Formatter的使用介绍
一、类型转换和格式化的介绍
在Spring MVC框架中需要收集用户的请求参数,并把请求的参数传递给到控制器,这里会存在一个小问题,这种方式中所有的请求参数类型只能是字符串类型,但是Java是强类型语言,所以Spring MVC中相应地提供了把这些字符串请求参数转换成相应的数据类型的功能。
Converter
Spring MVC 中的Converter<S,T>是一种可以把一种数据类型转换成另一种数据类型的接口,S表示源类型,T表示目标类型
Spring MVC中内置类型转换器
对于常用的类据类型,开发者不用创建自己的类型转换器,Spring MVC框架提供了如下几种内置类型转换器
1.标量转换器
类型
|
说明
|
StringToBooleanConverter
|
String--->boolean
|
ObjectToStringConverter
|
Object--->String,调用的是toString方法转换
|
StringToNumberConverterFactory
|
String---->数字(Integer、Long……)
|
NumberToNumberConverterFactory
|
基本数字类型--->数字包装类型
|
StringToCharacterConverter
|
String--->Character
|
NumberToCharacterConverter
|
基本数字类型--->Character
|
CharacterToNumberFactory
|
Character--->基本数字类型
|
StringToEnumConverterFactory
|
String--->枚举类型,通过Enum.valueOf把字符串转为需要的枚举类型
|
StringToLocaleConverter
|
String--->java.util.Locale
|
PropertiesToStringConverter
|
Properties--->String,默认通过ISO-8859-1编码
|
StringToPropertiesConverter
|
String--->Properties转换,默认使用ISO-8859-1编码
|
2.集合、数组相关转换器
类型
|
说明
|
ArrayToCollectionConverter
|
任意数组--->任意集合(List、Set)转换
|
CollectionToArrayConverter
|
任意集合(List、Set)--->任意数组转换
|
ArrayToArrayConverter
|
任意数组--->任意数组
|
CollectionToCollectionConverter
|
任意集合--->任意集合
|
MapToMapConverter
|
Map--->Map
|
ArrayToStringConverter
|
任意数组--->String
|
StirngToArrayConverter
|
String--->数组,默认通过“,”进行分隔,去掉字符串两边的空格
|
ArrayToObjectConverter
|
任意数组--->Object,如果目标类型与源类型兼容,直接返回源对象,否则返回数组第一个元素进行类型转换
|
ObjectToArrayConverter
|
Object--->单元素数组
|
CollectionToStringConverter
|
任意集合(List、Set)--->String
|
StringToCollectionConverter
|
String--->集合(List、Set),默认通过“,”进行分隔,去掉字符串两边的空格
|
CollectionToObjectConverter
|
任意集合--->Object,如果目标类型与源类型兼容,直接返回源对象,否则返回集合第一个元素进行类型转换
|
ObejctToCollectionConverter
|
Object--->单元素集合
|
类型转换发生的时机:在视图与控制器转递数据时发生。
Spring MVC对于基本类型(int,long,float,double,boolean,char……)做好了基本类型转换,但是在类型上要做到兼容。
自定义类型转换器
例如:在表单中输入商品信息,“杯具1,128.00,100”--->自动创建一个Goods对象,并且把"杯具1"=》goodsName;"128.00"=》goodsPrice;"100"=>goodsNumber
Eclipse中新增一个Maven的war工程
右建工程Build Path..,把web工程的Server Runtime加入进来
pom.xml中添加如下jar包依赖
<
dependencies
>
<!-- context -->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-context
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<!-- Spring MVC -->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-
webmvc
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<!-- Spring web -->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-web
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<!-- commons-logging -->
<
dependency
>
<
groupId
>commons-logging
</
groupId
>
<
artifactId
>commons-logging
</
artifactId
>
<
version
>1.2
</
version
>
</
dependency
>
<!-- spring-
aop
-->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-
aop
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<
dependency
>
<
groupId
>javax.servlet
</
groupId
>
<
artifactId
>
jstl
</
artifactId
>
<
version
>1.2
</
version
>
</
dependency
>
</
dependencies
>
创建实体类:com.xiaoxie.pojo.GoodsModel
package com.xiaoxie.pojo;
/**
* 类型转换中使用到的实体类
*
@author
adven
*
*/
public
class GoodsModel {
private String
goodsName;
private
double
goodsPrice;
private
int
goodsNumber;
//getter和setter
public String getGoodsName() {
return
goodsName;
}
public
void setGoodsName(String
goodsName) {
this.
goodsName =
goodsName;
}
public
double getGoodsPrice() {
return
goodsPrice;
}
public
void setGoodsPrice(
double
goodsPrice) {
this.
goodsPrice =
goodsPrice;
}
public
int getGoodsNumber() {
return
goodsNumber;
}
public
void setGoodsNumber(
int
goodsNumber) {
this.
goodsNumber =
goodsNumber;
}
//toString
@Override
public String toString() {
return
"GoodsModel[goodsName=" +
goodsName +
",goodsPrice=" +
goodsPrice +
",goodsNumber=" +
goodsNumber +
"]";
}
}
创建控制器类com.xiaoxie.controller.ConverterController
package com.xiaoxie.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.xiaoxie.pojo.GoodsModel;
@Controller
@RequestMapping(
"/my")
public
class ConverterController {
@RequestMapping(
"/converter")
public String myConverter(
@RequestParam(
"goods") GoodsModel
gm,Model
model) {
model.addAttribute(
"goods",
gm);
return
"show_goods";
}
}
自定义类型转换器类:com.xiaoxie.converter.GoodsConverter
package com.xiaoxie.converter;
import org.springframework.core.convert.converter.Converter;
import com.xiaoxie.pojo.GoodsModel;
public
class GoodsConverter
implements Converter<String,GoodsModel> {
@Override
public GoodsModel convert(String
arg0) {
//创建一个GoodsModel的实例
GoodsModel
gm =
new GoodsModel();
String
stringValue[] =
arg0.split(
",");
//判断StringValue中的值
if(
stringValue !=
null &&
stringValue.
length == 3) {
//为
gm
这个对象进行属性赋值
gm.setGoodsName(
stringValue[0]);
gm.setGoodsPrice(Double.
parseDouble(
stringValue[1]));
gm.setGoodsNumber(Integer.
parseInt(
stringValue[2]));
return
gm;
}
else {
throw
new IllegalArgumentException(String.
format(
"类型转换失败,需要格式:'商品名,100.00,1',但提供格式为:%s",
arg0));
}
}
}
添加Spring MVC的配置文件springmvc.xml
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns:context=
"http://www.springframework.org/schema/context"
xmlns:mvc=
"http://www.springframework.org/schema/mvc"
xsi:schemaLocation=
"http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"
>
<!-- 配置spring要自动扫描的包 -->
<
context:component-scan
base-package=
"com.xiaoxie.controller"
/>
<!-- 注册类型转换器GoodsConverter -->
<
bean
class=
"org.springframework.context.support.ConversionServiceFactoryBean"
id=
"conversionServiceFactoryBean"
>
<
property
name=
"
converters"
>
<
list
>
<
bean
class=
"com.xiaoxie.converter.GoodsConverter"
/>
</
list
>
</
property
>
</
bean
>
<
mvc:annotation-driven
conversion-service=
"conversionServiceFactoryBean"
/>
<!-- 配置视图解析器 -->
<
bean
class=
"org.springframework.web.servlet.view.InternalResourceViewResolver"
id=
"viewResolver"
>
<!-- 拼接前缀 -->
<
property
name=
"
prefix"
value=
"/WEB-INF/jsp/"
/>
<!-- 拼接后缀 -->
<
property
name=
"
suffix"
value=
".jsp"
/>
</
bean
>
</
beans
>
配置web.xml文件,主要是配置DispatcherServlet
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
<
web-app
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns=
"http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation=
"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id=
"WebApp_ID"
version=
"2.5"
>
<!-- DispatcherServlet配置 -->
<
servlet
>
<
servlet-name
>
springmvc
</
servlet-name
>
<
servlet-class
>org.springframework.web.servlet.DispatcherServlet
</
servlet-class
>
<!-- 加载spring
mvc
的配置文件 -->
<
init-param
>
<
param-name
>contextConfigLocation
</
param-name
>
<!-- 配置SpringMVC配置文件的路径 -->
<
param-value
>classpath:springmvc.xml
</
param-value
>
</
init-param
>
<!--
tomcat
容器在启动时加载
servlet
-->
<
load-on-startup
>1
</
load-on-startup
>
</
servlet
>
<
servlet-mapping
>
<
servlet-name
>
springmvc
</
servlet-name
>
<!--
servlet
处理请求地址,这里/表示所有
url
-->
<
url-pattern
>/
</
url-pattern
>
</
servlet-mapping
>
<!-- 配置解决
psot
请求中文乱码问题 -->
<
filter
>
<
filter-name
>CharacterEncodingFilter
</
filter-name
>
<
filter-class
>org.springframework.web.filter.CharacterEncodingFilter
</
filter-class
>
<
init-param
>
<
param-name
>encoding
</
param-name
>
<
param-value
>
utf-8
</
param-value
>
</
init-param
>
<
init-param
>
<
param-name
>forceEncoding
</
param-name
>
<
param-value
>true
</
param-value
>
</
init-param
>
</
filter
>
<
filter-mapping
>
<
filter-name
>CharacterEncodingFilter
</
filter-name
>
<
url-pattern
>/*
</
url-pattern
>
</
filter-mapping
>
</
web-app
>
新增一个index/input的Controller请求的Controller:com.xiaoxie.controller.IndexController
package com.xiaoxie.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping(
"index")
public
class IndexController{
@RequestMapping(
"input")
public String input() {
return
"input";
}
}
注意:这个controller只做了一个跳转不做任何处理,它经过视图解析器处理后跳转到/WEB-INF/jsp/input.jsp
新增index.jsp,在WebContent目录下
<%@
page
language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
<!
DOCTYPE
html
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<
html
>
<
head
>
<
meta
http-equiv=
"Content-Type"
content=
"text/html; charset=UTF-8"
>
<
title
>Insert title here
</
title
>
</
head
>
<
body
>
录入商品信息:
<
a
href=
"${pageContext.request.contextPath }
/index/input"
>录入
</
a
>!
<
br
/>
</
body
>
</
html
>
在/WEB-INF/jsp/目录下新增input.jsp
<%@
page
language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
<!
DOCTYPE
html
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<
html
>
<
head
>
<
meta
http-equiv=
"Content-Type"
content=
"text/html; charset=UTF-8"
>
<
title
>Insert title here
</
title
>
</
head
>
<
body
>
<
form
action=
"${pageContext.request.contextPath }
/my/converter"
method=
"post"
>
请输入商品信息(格式:商品名1,100.00,1):
<
input
type=
"text"
name=
"goods"
><
br
/>
<
input
type=
"submit"
value=
"提交"
/>
</
form
>
</
body
>
</
html
>
在/WEB-INF/jsp/目录下新增show_goods.jsp
<%@
page
language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
<!
DOCTYPE
html
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<
html
>
<
head
>
<
meta
http-equiv=
"Content-Type"
content=
"text/html; charset=UTF-8"
>
<
title
>Insert title here
</
title
>
</
head
>
<
body
>
创建商品信息如下:
<
br
/>
<!-- 使用model绑定的对象展示相关信息 -->
商品名称:${goods.goodsName }
<
br
/>
商品价格:${goods.goodsPrice }
<
br
/>
商品数量:${goods.goodsNumber }
</
body
>
</
html
>
运行项目打开index.jsp,点击对应的链接---->input.jsp---->录入信息---->show_goods.jsp
注意:在类型转换的过程中如报错我们看到Throw了一个异常,这个时候项目会出现400的异常页面
Formatter
Spring MVC框架中Formatter<T>与Converter<S,T>一样,也是一个可以把一种数据类型转为另一种数据类型的接口,这里注意它没有S(源类型),只有T(目标类型),这里的源类型必须要是String类型。
现论上Converter就可以覆盖到Formatter那为什么还有一个Formatter呢?
在Web项目中HTTP请求发送的数据到控制器中都是以String类型获取的,因而在Web项目中使用Formatter比Converter更合理。
Spring MVC提供的内置转换器
类型
|
说明
|
NumberFormatter
|
Number与String之间的解析与格式化
|
CurrencyFormatter
|
带货币符号的Number与String之间的解析与格式化
|
PercentFormatter
|
带百分符号的Number与String之间的解析与格式化
|
DateFormatter
|
Date与String之间的解析与格式化
|
自定义格式转换器
自定义格式转换器需要编写一个实现org.springframework.format.Formatter接口的Java类
这个接口有parse和print两个接口方法,在自定义的实现类中需要覆盖
parse方法:利用指定的Locale把一个String类型转换为目标类型
print方法:用于返回目标对象的字符串表示
在工程中新增一个实体Model:com.xiaoxie.pojo.EmployeeModel
package com.xiaoxie.pojo;
import java.util.Date;
public
class EmployeeModel {
private String
workNo;
private String
name;
private Date
joinDate;
private
double
salary;
private String
position;
/*getter,setter*/
public String getWorkNo() {
return
workNo;
}
public
void setWorkNo(String
workNo) {
this.
workNo =
workNo;
}
public String getName() {
return
name;
}
public
void setName(String
name) {
this.
name =
name;
}
public Date getJoinDate() {
return
joinDate;
}
public
void setJoinDate(Date
joinDate) {
this.
joinDate =
joinDate;
}
public
double getSalary() {
return
salary;
}
public
void setSalary(
double
salary) {
this.
salary =
salary;
}
public String getPosition() {
return
position;
}
public
void setPosition(String
position) {
this.
position =
position;
}
@Override
public String toString() {
return
"Employee[工号=" +
workNo +
",姓名=" +
name +
",职位=" +
position +
",入职日期=" +
joinDate +
",工资=" +
salary +
"]";
}
}
新增一个自定义的转换器:com.xiaoxie.converter.MyFormatter
package com.xiaoxie.converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.springframework.format.Formatter;
public
class MyFormatter
implements Formatter<Date> {
//使用记录日志的对象
private
static
final Log
logger = LogFactory.
getLog(MyFormatter.
class);
//做日期格式化使用SimpleDateFormat
private SimpleDateFormat
sdf =
new SimpleDateFormat(
"yyyy-MM-dd");
@Override
public String print(Date
object, Locale
locale) {
return
sdf.format(
object);
}
@Override
public Date parse(String
text, Locale
locale)
throws ParseException {
logger.info(
"把字符串转为Date对象");
return
sdf.parse(
text);
}
}
把转换器注册到Spring MVC,在Spring MVC的配置文件中新增如下内容
<!-- 注册Formatter -->
<
bean
class=
"org.springframework.format.support.FormattingConversionServiceFactoryBean"
id=
"formattingConversionServiceFactoryBean"
>
<
property
name=
"
formatters"
>
<
set
>
<
bean
class=
"com.xiaoxie.converter.MyFormatter"
></
bean
>
</
set
>
</
property
>
</
bean
>
<
mvc:annotation-driven
conversion-service=
"formattingConversionServiceFactoryBean"
/>
新增EmployeeController,com.xiaoxie.controller.EmployeeController
package com.xiaoxie.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.xiaoxie.pojo.EmployeeModel;
@Controller
@RequestMapping(
"/employee")
public
class EmployeeController {
@RequestMapping(
"/create")
public String createEmp(EmployeeModel
em,Model
model) {
model.addAttribute(
"employee",
em);
return
"show_emp";
}
}
从这里可以看到当请求/employee/create时会转发到视图 show_emp.jsp
对IndexController进行修改,提供一个请求,便于转发到员工信息录入界面,其中添加方法如下
@RequestMapping(
"/input_emp")
public String inputEmp() {
return
"input_emp";
}
修改index.jsp(WebContent下)
<%@
page
language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
<!
DOCTYPE
html
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<
html
>
<
head
>
<
meta
http-equiv=
"Content-Type"
content=
"text/html; charset=UTF-8"
>
<
title
>Insert title here
</
title
>
</
head
>
<
body
>
录入商品信息:
<
a
href=
"${pageContext.request.contextPath }
/index/input"
>录入
</
a
>!
<
br
/>
<
hr
/>
录入员工信息:
<
a
href=
"${pageContext.request.contextPath }
/index/input_emp"
>录入
</
a
>!
<
br
/>
</
body
>
</
html
>
新增input_emp.jsp(根据我们配置在spirngmvc的配置文件的视图解析器可知我们要配置到/WEB-INF/jsp/input_emp.jsp)
<%@
page
language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
<!
DOCTYPE
html
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<
html
>
<
head
>
<
meta
http-equiv=
"Content-Type"
content=
"text/html; charset=UTF-8"
>
<
title
>Insert title here
</
title
>
</
head
>
<
body
>
<
form
action=
"${pageContext.request.contextPath }
/employee/create"
method=
"post"
>
工号:
<
input
type=
"text"
name=
"workNo"
/>
<
br
/>
姓名:
<
input
type=
"text"
name=
"name"
/>
<
br
/>
入职日期:
<
input
type=
"text"
name=
"joinDate"
/>(
yyyy-MM-
dd)
<
br
/>
职位:
<
input
type=
"text"
name=
"position"
/>
<
br
/>
工资:
<
input
type=
"text"
name=
"salary"
/>
<
br
/>
<
input
type=
"submit"
value=
"录入"
/>
</
form
>
</
body
>
</
html
>
新增show_emp.jsp(根据我们配置在spirngmvc的配置文件的视图解析器可知我们要配置到/WEB-INF/jsp/show_emp.jsp)
<%@
page
language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
<!
DOCTYPE
html
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<
html
>
<
head
>
<
meta
http-equiv=
"Content-Type"
content=
"text/html; charset=UTF-8"
>
<
title
>Insert title here
</
title
>
</
head
>
<
body
>
您录入的员工信息如下:
<
br
/>
工号:${employee.workNo }
<
br
/>
姓名:${employee.name }
<
br
/>
入职日期:
<
fmt:formatDate
value=
"${employee.joinDate }
"
pattern=
"yyyy-MM-dd"
/><
br
/>
职位:${employee.position }
<
br
/>
工资:${employee.salary }
<
br
/>
</
body
>
</
html
>
程序运行时我们可以看到,在提交员工信息时要转为一个EmployeeModel对象,这个时候会调用自定义的Formatter类进行字符串转Date的动作,且在控制台可以看到打印出来的信息:
信息: 把字符串转为Date对象