您的位置:首页 > 编程语言 > Java开发

第五章:数据格式化——深入浅出学Spring Web MVC

2015-11-04 11:49 731 查看
数据格式化简介
在实际项目中,通常需要将数据转换为具有某种格式的字符串进行展示,因此Spring3引入了格式化转换器(Formatter SPI) 和格式化服务API(FormattingConversionService)来支持这种需求。

在Spring中它和PropertyEditor功能类似,可以替代PropertyEditor来进行对象的解析和格式化,而且支持细粒度的字段级别的格式化/解析。

nFormatter SPI简介
Formatter SPI核心是完成解析和格式化转换逻辑,在如Web应用/客户端项目中,需要解析、打印/展示本地化的对象值时使用,如根据Locale信息将java.util.Date---->java.lang.String打印/展示、java.lang.String---->java.util.Date等。

该格式化转换系统是Spring通用的,其定义在org.springframework.format包中,不仅仅在Spring Web MVC场景下用。

nDateFormatter
java.util.Date<---->String,实现日期的格式化/解析
nNumberFormatter
java.lang.Number<---->String,实现通用样式的格式化/解析
nCurrencyFormatter
java.lang.BigDecimal<---->String,实现货币样式的格式化/解析
nPercentFormatter
java.lang.Number<---->String,实现百分数样式的格式化/解析
nNumberFormatAnnotationFormatterFactory
@NumberFormat注解类型的数字字段类型<---->String
①通过@NumberFormat指定格式化/解析格式
②可以格式化/解析的数字类型:Short、Integer、Long、Float、Double、BigDecimal、BigInteger

nJodaDateTimeFormatAnnotationFormatterFactory
@DateTimeFormat注解类型的日期字段类型<---->String
①通过@DateTimeFormat指定格式化/解析格式
②可以格式化/解析的日期类型:joda中的日期类型(org.joda.time包中的):LocalDate、LocalDateTime、LocalTime、ReadableInstant,java内置的日期类型:Date、Calendar、Long

注意:classpath中必须有Joda-Time类库,否则无法格式化日期类型
n提示:
NumberFormatAnnotationFormatterFactory和JodaDateTimeFormatAnnotationFormatterFactory(如果classpath提供了Joda-Time类库)在使用格式化服务实现DefaultFormattingConversionService时会自动注册。

n环境准备
在示例之前,需要到 http://joda-time.sourceforge.net/下载Joda-Time类库,这里使用的是joda-time-2.1版本,将这个jar包添加到classpath
n类型级别的解析/格式化
一、直接使用Formatter SPI进行解析/格式化
//CurrencyFormatter:实现货币样式的格式化/解析
CurrencyFormatter currencyFormatter = new CurrencyFormatter();
currencyFormatter.setFractionDigits(2);//保留小数点后几位
currencyFormatter.setRoundingMode(RoundingMode. CEILING);//舍入模式(
ceilling
表示四舍五入)
//1、将带货币符号的字符串“$123.125”转换为BigDecimal("123.00")
BigDecimal b1 = currencyFormatter.parse("$123.125", Locale. US);
//2、将BigDecimal("123")格式化为字符串“$123.00”展示
String s1 = currencyFormatter.print(new BigDecimal("123"), Locale. US);

String s2 = currencyFormatter.print(new BigDecimal("123"), Locale. CHINA);

System. out.println("b1="+b1+",s1="+s1+",s2="+s2);

parse方法:将带格式的字符串根据Locale信息解析为相应的BigDecimal类型数据;
print方法:将BigDecimal类型数据根据Locale信息格式化为字符串数据进行展示。

不同于Convert SPI,Formatter SPI可以根据本地化(Locale)信息进行解析/格式化。

n 二:使用DefaultFormattingConversionService进行解析/格式化
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();

//默认不自动注册任何Formatter
CurrencyFormatter currencyFormatter = new CurrencyFormatter();

currencyFormatter.setFractionDigits(2);//保留小数点后几位
currencyFormatter.setRoundingMode(RoundingMode. CEILING);//舍入模式(
ceilling
表示四舍五入)
//注册Formatter SPI实现
conversionService.addFormatter(currencyFormatter);

//绑定Locale信息到ThreadLocal
//FormattingConversionService内部自动获取作为Locale信息,如果不设值默认是 Locale.getDefault()
LocaleContextHolder. setLocale(Locale. US);
String s1 = conversionService.convert( new BigDecimal("1234.128"), String.
class);
LocaleContextHolder. setLocale( null);

LocaleContextHolder. setLocale(Locale. CHINA);
String s2 = conversionService.convert( new BigDecimal("1234.128"), String.
class);
BigDecimal b1 = conversionService.convert("¥1,234.13", BigDecimal. class);

LocaleContextHolder. setLocale( null);
System. out.println("s1="+s1+",s2="+s2+",b1="+b1);

n 说明:
DefaultFormattingConversionService:
带数据格式化功能的类型转换服务实现;
conversionService.addFormatter():
注册Formatter SPI实现;
conversionService.convert(new BigDecimal(“1234.128”), String. class):

用于将BigDecimal类型数据格式化为字符串类型,此处根据“LocaleContextHolder. setLocale(locale)”设置的本地化信息进行格式化;

conversionService.convert(“¥1,234.13”, BigDecimal. class):
用于将字符串类型数据解析为BigDecimal类型数据,此处也是根据“LocaleContextHolder. setLocale(locale)”设置的本地化信息进行解;

LocaleContextHolder. setLocale(locale):
设置本地化信息到ThreadLocal,以便Formatter SPI根据本地化信息进行解析/格式化;

n前面学习了类型级别的解析/格式化,从测试用例可以看出类型级别的是对项目中的整个类型实施相同的解析/格式化逻辑。
有的同学可能需要在不同的类的字段实施不同的解析/格式化逻辑,如用户模型类的注册日期字段只需要如“2013-05-02”格式进行解析/格式化即可,而订单模型类的下订单日期字段可能需要如“2013-05-02 20:13:13”格式进行展示。

这个就需要进行字段级别的解析/格式化了。
n一、使用内置的注解进行字段级别的解析/格式化
1:首先准备测试用的 model类
public class FormatterModel {
@NumberFormat(style=Style. NUMBER, pattern="#,###")
private int totalCount;
@NumberFormat(style=Style. PERCENT)
private double discount;
@NumberFormat(style=Style. CURRENCY)
private double sumMoney;
@DateTimeFormat(iso=ISO. DATE)
private Date registerDate;
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date orderDate;
//省略getter/setter }

n示例说明
此处我们使用了Spring字段级别解析/格式化的两个内置注解:
1:@Number:定义数字相关的解析/格式化元数据(通用样式、货币样式、百分数样式),参数如下:
(1)style:用于指定样式类型,包括三种:Style.NUMBER(通用样式) Style.CURRENCY(货币样式) Style.PERCENT(百分数样式),默认Style.NUMBER;

(2)pattern:自定义样式,如patter=“#,###”;
2:@DateTimeFormat:定义日期相关的解析/格式化元数据,参数如下:
(1)pattern:指定解析/格式化字段数据的模式,如”yyyy-MM-dd HH:mm:ss”
(2)iso:指定解析/格式化字段数据的ISO模式,包括四种:ISO.NONE(不使用) ISO.DATE(yyyy-MM-dd) ISO.TIME(hh:mm:ss.SSSZ) ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ),默认ISO.NONE;

(3)style:指定用于格式化的样式模式,默认“SS”,具体使用请参考Joda-Time类库的org.joda.time.format.DateTimeFormat的forStyle的javadoc;

优先级: pattern 大于 iso 大于 style。

n测试代码
//默认自动注册对@NumberFormat和@DateTimeFormat的支持
DefaultFormattingConversionService conversionService =
new DefaultFormattingConversionService();
//准备测试模型对象
FormatterModel model = new FormatterModel();
model.setTotalCount(10000);
model.setDiscount(0.51);
model.setSumMoney(10000.13);
model.setRegisterDate(new Date(2013-1900, 4, 1));
model.setOrderDate(new Date(2013-1900, 4, 1, 20, 18, 18));

//获取类型信息
TypeDescriptor descriptor =
new TypeDescriptor(FormatterModel.class.getDeclaredField("totalCount"));
TypeDescriptor stringDescriptor = TypeDescriptor. valueOf(String.class);

String s1 = (String)conversionService.convert(model.getTotalCount(), descriptor, stringDescriptor);

int a = (Integer)conversionService.convert("10,000", stringDescriptor, descriptor);

System. out.println("s1=="+s1+" , a="+a);

n测试说明
1:TypeDescriptor:拥有类型信息的上下文,用于Spring3类型转换系统获取类型信息的(可以包含类、字段、方法参数、属性信息);通过TypeDescriptor,我们就可以获取(类、字段、方法参数、属性)的各种信息,如注解类型信息;

2:conversionService.convert(model.getTotalCount(), descriptor, stringDescriptor):将totalCount格式化为字符串类型,此处会根据totalCount字段的注解信息(通过descriptor对象获取)来进行格式化;

3:conversionService.convert("10,000", stringDescriptor, descriptor):将字符串“10,000”解析为totalCount字段类型,此处会根据totalCount字段的注解信息(通过descriptor对象获取)来进行解析。

n继续测试,可以为不同的字段指定不同的注解信息进行字段级别的细粒度数据解析/格式化 ,测试如下:
TypeDescriptor stringDescriptor = TypeDescriptor. valueOf(String.class);

TypeDescriptor descriptor = new TypeDescriptor(FormatterModel.class.getDeclaredField("registerDate"));

String s1 = (String)conversionService.convert(model.getRegisterDate(), descriptor, stringDescriptor);

Date d1 = (Date)conversionService.convert("2013-05-01", stringDescriptor, descriptor);

descriptor = new TypeDescriptor(FormatterModel.class.getDeclaredField("orderDate"));

String s2 = (String)conversionService.convert(model.getOrderDate(), descriptor, stringDescriptor);

Date d2 = (Date)conversionService.convert("2013-05-01 20:18:18", stringDescriptor, descriptor);

System. out.println("s1=="+s1+" , d1="+d1+" , s2="+s2+" , d2="+d2);

通过如上测试可以看出,我们可以通过字段注解方式实现细粒度的数据解析/格式化控制,但是必须使用TypeDescriptor来指定类型的上下文信息,即编程实现字段的数据解析/格式化比较麻烦。

n如果没有自定义的Formatter的话,spring的配置跟前面讲数据转换是一样的
n测试用的Controller
@Controller
public class FormatterController {
@RequestMapping(value = "/tf")
public String testFormatter(@ModelAttribute("model") FormatterModel fm){
return "format/t"; } }
n展示结果的t.jsp ,使用了两种方式
<%@taglib prefix= "spring" uri= "http://www.springframework.org/tags" %>

<%@taglib prefix= "form" uri= "http://www.springframework.org/tags/form" %>

totalCount:<spring:bind path= "model.totalCount">${status.value}</spring:bind><br/>

discount:<spring:bind path= "model.discount">${status.value}</spring:bind><br/>

sumMoney:<spring:bind path= "model.sumMoney">${status.value}</spring:bind><br/>

<br/><br/>
<form:form commandName= "model">
<form:input path= "sumMoney"/>
</form:form>

n测试用的url
在浏览器输入类似如下的url,
http://localhost:9080/mvcexample/tf?totalCount=12345&discount=0.25&sumMoney=

123
就可以看到格式化后的结果了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: