1.使用Formatter格式化数据

6.3 数据格式化

Spring使用Converter转换器进行源类型对象到目标类型对象的转换,Spring的转换器并不承担输入以及输出信息格式化的工作。如果需要转换的源类型数据是从客户端界面中传过来的,比如日期、时间、数字、货币等数据,它们往往都拥有一定的格式。在不同的本地化环境中,同一类型的数据还会相应地呈现不同的显示格式

如何从格式化的数据中获取真正的数据以完成数据绑定,并将处理完成的数据输出为格式化的数据是Spring格式化框架需要解决的问题。Spring从3.0开始引入了格式化转换框架,这个框架位于org.springframework.format包中,其中最重要的是Formatter<T>接口

Converter和Formatter的区别

Converter完成任意ObjectObject之间的类型转换,
Formatter完成任意ObjectString之间的类型转换,即格式化和解析,
它和PropertyEditor功能类似,可以替代PropertyEditor来进行对象的解析和格式化,而且支持细粒度的字段级别的格式化解析。
Formatter只能将String转换成另一种Java类型。例如,将String转换成Date但它不能将Long转换成Date
因此Formatter更适用于Web层的数据转换。而Converter则可以用在任意层中
因此,在Spring MVC的应用程序当中,如果想转换表单中的用户输入,则建议选择Formatter,而不是Converter.

Spring MVC格式化接口

Formatter格式化转换是Spring通用的,定义在org.springframework.format包中,其不仅仅在Spring Web MVC场景下使用。在org.springframework.format包中定义的接口如下:

Printer接口

Printer<T>接口。格式化显示接口,其将T类型的对象根据 Locale信息以某种格式进行打印显示(即返回字符串形式)。该接口中定义了一个 print方法,其根据本地化信息将T类型的对象输出为不同格式的字符串

1
String print(T object,Locale locale)

Parser接口

Parser<T>接口。解析接口,其根据 Locale信息解析字符串到T类型的对象。该接口中定义了一个 parse方法,其参考本地化信息将一个格式化的字符串转换为T类型的对象

1
T parse(String text,Locale locale) throws ParseException

Formatter接口

Formatter<T>接口。格式化接口,继承自 Printer<T>Parser<T>接口,它完成T类型对象的格式化和解析功能。

FormatterRegistrar接口

FormatterRegistrar接口。注册格式化转换器。该接口中定义registerFormatters方法,其参数就是 FormatterRegistry对象,用于注册多个格式化转换器

1
void registerFormatters(FormatterRegistry registry)

AnnotationFormatterFactory接口

AnnotationFormatterFactory< A extends Annotation>接口。注解驱动的字段格式化工厂,用于创建带注解的对象字段的 PrinterParser,即用于格式化和解析带注解的对象字段。该接口中定义了以下几个方法:

  • Set<Class<?>> getFieldTypes()。注解A的应用范围,即哪些属性类可以标注A注解
  • Printer<?> getPrinter( a Annotation, Class<?> fieldType)。**根据注解A获取特定属性类型Printer**。
  • Parser<?> getParser( A annotation, Class<?> fieldType)。根据注解A获取特定属性类型的Parser

示例 使用Formatter格式化数据

使用自定义的格式化类

DateFormatter

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
27
28
29
30
31
32
// 实现Converter<S,T>接口
public class DateFormatter
implements Formatter<Date>
{
// 日期格式化对象
private SimpleDateFormat dateFormat;
// 构造器,通过依赖注入的日期类型创建日期格式化对象
public DateFormatter(String datePattern)
{
this.dateFormat = new SimpleDateFormat(datePattern);
}
// 显示Formatter<T>的T类型对象
@Override
public String print(Date date, Locale locale)
{
System.out.println("实现的print方法:Date转String");
return dateFormat.format(date);
}
// 解析文本字符串返回一个Formatter<T>的T类型对象。
@Override
public Date parse(String source, Locale locale) throws ParseException
{
try
{
System.out.println("实现的parse方法:String转Date");
return dateFormat.parse(source);
} catch (Exception e)
{
throw new IllegalArgumentException();
}
}
}

DateFormatter类实现了org. springframework. format. Formatter接口。实现了接口中的两个方法:

  • parse方法,使用指定的 Locale将一个 String解析成目标T类型;
  • print方法,用于返回T类型的字符串表示形式。

DateFormatter类中使用 SimpleDateFormat对象将 String转换成Date类型,日期类型模板yyy-M-dd会通过配置文件的依赖注入设置。

springmvc-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 装配自定义格式化 -->
<mvc:annotation-driven conversion-service="conversionService" />
<!-- 格式化 -->
<bean
id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!-- formatters属性可以注册格式化类Fommatter -->
<!-- converters属性可以注册转换器Converter -->
<property name="formatters">
<list>
<!-- 使用自定义格式化类 -->
<!-- 指定格式化模板 -->
<bean
class="org.fkit.formatter.DateFormatter"
c:_0="yyyy-MM-dd" />
</list>
</property>
</bean>

Spring在格式化模块中定义了一个实现ConversionService接口的FormattingConversionService实现类,该类既具有类型转换功能,又具有格式化的功能
FormattingConversionServiceFactoryBean工厂类用于在 Spring上下文中构造一个FormattingConversionService对象,通过这个工厂类可以注册自定义的格式化转换器

以上配置使用FormattingConversionServiceFactoryBean对自定义的格式化转换器DateFormatter进行了注册。 FormattingConversionServiceFactoryBean类有一个属性converters,可以用它注册Converter;有一个属性formatters,可以用它注册Formatter
值得注意的是,在mvc:annotation-driven标签内部默认创建的conversionService实例就是一个FormattingConversionServiceFactoryBean,有了FormattingConversionServiceFactoryBean之后, Spring MVC对处理方法的参数就绑定格式化功能了。

User.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 域对象,实现序列化接口
public class User
implements Serializable
{
private static final long serialVersionUID = 1L;
private String loginname;
private Date birthday;
public User()
{
super();
// TODO Auto-generated constructor stub
}
// 此处省略getter和setter方法,请自己补上
@Override
public String toString()
{
return "User [loginname=" + loginname + ", birthday=" + birthday + "]";
}
}

UserController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Controller
public class UserController {
@GetMapping(value = "/registerForm")
public String registerForm()
{
// 跳转到注册页面
return "registerForm";
}
@PostMapping(value = "/register")
public String register(@ModelAttribute
User user, Model model)
{
System.out.println(user);
model.addAttribute("user", user);
return "success";
}
}

registerForm.jsp

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
27
28
29
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试Formatter接口</title>
</head>
<body>
<h3>注册页面</h3>
<form action="register" method="post">
<table>
<tr>
<td><label>登录名: </label></td>
<td><input type="text" id="loginname"
name="loginname"></td>
</tr>
<tr>
<td><label>生日: </label></td>
<td><input type="text" id="birthday"
name="birthday"></td>
</tr>
<tr>
<td><input id="submit" type="submit" value="登录"></td>
</tr>
</table>
</form>
</body>
</html>

success.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>测试Formatter</title>
</head>
<body>
登录名:${requestScope.user.loginname }
<br> 生日:
<fmt:formatDate value="${requestScope.user.birthday}"
pattern="yyyy年MM月dd日" />
<br>
</body>
</html>

测试

测试链接

1
<a href="registerForm">测试使用格式化类</a>

填写数据

这里有一张图片

转换效果

这里有一张图片

控制台输出

1
2
实现的parse方法:String转Date
User [loginname=xiaoming, birthday=Sat Feb 03 00:00:00 CST 2345]

使用Spring提供的格式化类

以上使用实现Formatter<T>接口的方式完成数据转换,而Spring本身提供了很多常用的Formatter实现。在org.springframework.format.datetime包中提供了一个用于时间对象格式化的DateFormatter实现类
org.springframework.format.number包中提供了3个用于数字对象格式化的实现类:

  • Numberformatter。用于数字类型对象的格式化
  • CurrencyFormatter。用于货币类型对象的格式化PercentFormatter。用于百分数数字类型对象的格式化

例如,如果要使用org.springframework.format.datetime包中提供的DateFormatter实现类完成字符串到日期对象的转换,则只需要在配置文件中配置就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean
id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!-- formatters属性可以注册格式化类Fommatter -->
<!-- converters属性可以注册转换器Converter -->
<property name="formatters">
<list>
<!-- 使用Spring提供的日期格式化类 -->
<!-- 指定日期格式为yyy-MM-dd -->
<bean
class="org.springframework.format.datetime.DateFormatter"
p:pattern="yyyy-MM-dd" />
</list>
</property>
</bean>

运行效果与上面的类似.