SpringMVC的数据格式化
来源:互联网 发布:淘宝店铺降权查询 编辑:程序博客网 时间:2024/06/04 18:08
在如Web /客户端项目中,通常需要将数据转换为具有某种格式的字符串进行展示,因此上节我们学习的数据类型转换系统核心作用不是完成这个需求,因此Spring3引入了格式化转换器(Formatter SPI) 和格式化服务API(FormattingConversionService)从而支持这种需求。在Spring中它和PropertyEditor功能类似,可以替代PropertyEditor来进行对象的解析和格式化,而且支持细粒度的字段级别的格式化/解析。
Formatter SPI核心是完成解析和格式化转换逻辑,在如Web应用/客户端项目中,需要解析、打印/展示本地化的对象值时使用,如根据Locale信息将java.util.Date—->java.lang.String打印/展示、java.lang.String—->java.util.Date等。
架构
一共有如下两组四个接口:
Printer接口
Printer接口:格式化显示接口,将T类型的对象根据Locale信息以某种格式进行打印显示(即返回字符串形式);
package org.springframework.format; public interface Printer<T> { String print(T object, Locale locale); }
Parser接口
Parser接口:解析接口,根据Locale信息解析字符串到T类型的对象;
package org.springframework.format; public interface Parser<T> { T parse(String text, Locale locale) throws ParseException; }
解析失败可以抛出java.text.ParseException或IllegalArgumentException异常即可。
Formatter接口
格式化SPI接口,继承Printer和Parser接口,完成T类型对象的格式化和解析功能;
package org.springframework.format; public interface Formatter<T> extends Printer<T>, Parser<T> { }
AnnotationFormatterFactory接口
注解驱动的字段格式化工厂,用于创建带注解的对象字段的Printer和Parser,即用于格式化和解析带注解的对象字段.
package org.springframework.format; public interface AnnotationFormatterFactory<A extends Annotation> {//①可以识别的注解类型 Set<Class<?>> getFieldTypes();//②可以被A注解类型注解的字段类型集合 Printer<?> getPrinter(A annotation, Class<?> fieldType);//③根据A注解类型和fieldType类型获取Printer Parser<?> getParser(A annotation, Class<?> fieldType);//④根据A注解类型和fieldType类型获取Parser }
返回用于格式化和解析被A注解类型注解的字段值的Printer和Parser。如JodaDateTimeFormatAnnotationFormatterFactory可以为带有@DateTimeFormat注解的java.util.Date字段类型创建相应的Printer和Parser进行格式化和解析
格式化转换器注册器、格式化服务
提供类型转换器注册支持,运行时类型转换API支持。
一个有如下两种接口:
FormatterRegistry
FormatterRegistry:格式化转换器注册器,用于注册格式化转换器(Formatter、Printer和Parser、AnnotationFormatterFactory);
package org.springframework.format; public interface FormatterRegistry extends ConverterRegistry { //①添加格式化转换器(Spring3.1 新增API) void addFormatter(Formatter<?> formatter); //②为指定的字段类型添加格式化转换器 void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter); //③为指定的字段类型添加Printer和Parser void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser); //④添加注解驱动的字段格式化工厂AnnotationFormatterFactory void addFormatterForFieldAnnotation( AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory); }
FormattingConversionService
FormattingConversionService:继承自ConversionService,运行时类型转换和格式化服务接口,提供运行期类型转换和格式化的支持。
FormattingConversionService内部实现如下图所示:
我们可以看到FormattingConversionService内部实现如上所示,当你调用convert方法时:
- 若是S类型—–>String:调用私有的静态内部类PrinterConverter,其又调用相应的Printer的实现进行格式化;
- 若是String—–>T类型:调用私有的静态内部类ParserConverter,其又调用相应的Parser的实现进行解析;
- 若是A注解类型注解的S类型—–>String:调用私有的静态内部类AnnotationPrinterConverter,其又调用相应的AnnotationFormatterFactory的getPrinter获取Printer的实现进行格式化;
- 若是String—–>A注解类型注解的T类型:调用私有的静态内部类AnnotationParserConverter,其又调用相应的AnnotationFormatterFactory的getParser获取Parser的实现进行解析。
注:S类型表示源类型,T类型表示目标类型,A表示注解类型。
此处可以可以看出之前的Converter SPI完成任意Object与Object之间的类型转换,而Formatter SPI完成任意Object与String之间的类型转换(即格式化和解析,与PropertyEditor类似)。
Spring内建的格式化转换器
NumberFormatAnnotationFormatterFactory和JodaDateTimeFormatAnnotationFormatterFactory(如果classpath提供了Joda-Time类库)在使用格式化服务实现DefaultFormattingConversionService时会自动注册。
类型级别的解析/格式化
直接使用Formatter SPI进行解析/格式化
//二、CurrencyFormatter:实现货币样式的格式化/解析 CurrencyFormatter currencyFormatter = new CurrencyFormatter(); currencyFormatter.setFractionDigits(2);//保留小数点后几位 currencyFormatter.setRoundingMode(RoundingMode.CEILING);//舍入模式(ceilling表示四舍五入) //1、将带货币符号的字符串“$123.125”转换为BigDecimal("123.00") Assert.assertEquals(new BigDecimal("123.13"), currencyFormatter.parse("$123.125", Locale.US)); //2、将BigDecimal("123")格式化为字符串“$123.00”展示 Assert.assertEquals("$123.00", currencyFormatter.print(new BigDecimal("123"), Locale.US)); Assert.assertEquals("¥123.00", currencyFormatter.print(new BigDecimal("123"), Locale.CHINA)); Assert.assertEquals("¥123.00", currencyFormatter.print(new BigDecimal("123"), Locale.JAPAN));
parse方法:将带格式的字符串根据Locale信息解析为相应的BigDecimal类型数据;
print方法:将BigDecimal类型数据根据Locale信息格式化为字符串数据进行展示。
不同于Convert SPI,Formatter SPI可以根据本地化(Locale)信息进行解析/格式化。
自定义Formatter进行解析/格式化
定义Formatter SPI实现
//省略import public class PhoneNumberFormatter implements Formatter<PhoneNumberModel> { Pattern pattern = Pattern.compile("^(\\d{3,4})-(\\d{7,8})$"); @Override public String print(PhoneNumberModel phoneNumber, Locale locale) {//①格式化 if(phoneNumber == null) { return ""; } return new StringBuilder().append(phoneNumber.getAreaCode()).append("-") .append(phoneNumber.getPhoneNumber()).toString(); } @Override public PhoneNumberModel parse(String text, Locale locale) throws ParseException {//②解析 if(!StringUtils.hasLength(text)) { //①如果source为空 返回null return null; } Matcher matcher = pattern.matcher(text); if(matcher.matches()) { //②如果匹配 进行转换 PhoneNumberModel phoneNumber = new PhoneNumberModel(); phoneNumber.setAreaCode(matcher.group(1)); phoneNumber.setPhoneNumber(matcher.group(2)); return phoneNumber; } else { //③如果不匹配 转换失败 throw new IllegalArgumentException(String.format("类型转换失败,需要格式[010-12345678],但格式是[%s]", text)); } } }
测试用例:
@Test public void test() { DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(); conversionService.addFormatter(new PhoneNumberFormatter()); Assert.assertEquals("010-12345678", conversionService.convert(phoneNumber, String.class)); Assert.assertEquals("010", conversionService.convert("010-12345678", PhoneNumberModel.class).getAreaCode()); }
字段级别的解析/格式化
使用内置的注解进行字段级别的解析/格式化
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 }
@Number:定义数字相关的解析/格式化元数据(通用样式、货币样式、百分数样式),参数如下:
style:用于指定样式类型,包括三种:Style.NUMBER(通用样式) Style.CURRENCY(货币样式) Style.PERCENT(百分数样式),默认Style.NUMBER;
pattern:自定义样式,如patter=”#,###”;
@DateTimeFormat:定义日期相关的解析/格式化元数据,参数如下:
pattern:指定解析/格式化字段数据的模式,如”yyyy-MM-dd HH:mm:ss”
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;
style:指定用于格式化的样式模式,默认“SS”,具体使用请参考Joda-Time类库的org.joda.time.format.DateTimeFormat的forStyle的javadoc;
优先级: pattern 大于 iso 大于 style。
自定义注解进行字段级别的解析/格式化:
定义解析/格式化字段的注解类型:
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface PhoneNumber { }
实现AnnotationFormatterFactory注解格式化工厂:
//省略import public class PhoneNumberFormatAnnotationFormatterFactory implements AnnotationFormatterFactory<PhoneNumber> {//①指定可以解析/格式化的字段注解类型 private final Set<Class<?>> fieldTypes; private final PhoneNumberFormatter formatter; public PhoneNumberFormatAnnotationFormatterFactory() { Set<Class<?>> set = new HashSet<Class<?>>(); set.add(PhoneNumberModel.class); this.fieldTypes = set; this.formatter = new PhoneNumberFormatter();//此处使用之前定义的Formatter实现 } //②指定可以被解析/格式化的字段类型集合 @Override public Set<Class<?>> getFieldTypes() { return fieldTypes; } //③根据注解信息和字段类型获取解析器 @Override public Parser<?> getParser(PhoneNumber annotation, Class<?> fieldType) { return formatter; } //④根据注解信息和字段类型获取格式化器 @Override public Printer<?> getPrinter(PhoneNumber annotation, Class<?> fieldType) { return formatter; } }
集成到Spring Web MVC环境
注册FormattingConversionService实现和自定义格式化转换器:
package com.lf.web;import com.lf.web.config.MyFormatterRegistry;import com.lf.web.config.MyHttpMessage;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.format.FormatterRegistry;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.web.servlet.ViewResolver;import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;import org.springframework.web.servlet.config.annotation.EnableWebMvc;import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;import org.springframework.web.servlet.view.InternalResourceViewResolver;import java.util.List;/** * Created by LF on 2017/5/4. */@Configuration@EnableWebMvc@ComponentScan//组件扫描public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addFormatters(FormatterRegistry registry) { super.addFormatters(registry); registry.addFormatter(new PhoneNumberFormatter()); }}
示例:
(1、模型对象字段的数据解析/格式化:
@RequestMapping(value = "/format1") public String test1(@ModelAttribute("model") FormatterModel formatModel) { return "format/success"; }
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/> phoneNumber:<spring:bind path="model.phoneNumber">${status.value}</spring:bind><br/> <!-- 如果没有配置org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor将会报错 --> phoneNumber:<spring:eval expression="model.phoneNumber"></spring:eval><br/> <br/><br/> <form:form commandName="model"> <form:input path="phoneNumber"/><br/> <form:input path="sumMoney"/> </form:form>
- SpringMVC数据的格式化
- SpringMVC的数据格式化
- springmvc之数据的格式化
- SpringMvc中数据的格式化
- SpringMVC的数据转换、数据格式化
- springmvc数据的格式化及数据校验
- SpringMVC的数据格式化-注解驱动的属性格式化
- SpringMVC数据格式化
- SpringMVC-09 数据格式化
- springmvc数据格式化
- SpringMVC数据格式化
- SpringMVC 中 数据格式化
- SpringMVC数据格式化标签
- SpringMVC 数据的格式化、JSR 303数据校验和国际化
- SpringMVC返回JSON数据中日期类型数据的格式化
- SpringMVC返回的json数据Date(时间)格式化的问题
- springmvc 数据转换与格式化
- 第七章 注解式控制器的数据验证、类型转换及格式化 SpringMVC数据格式化
- REST架构风格
- <Return>键
- Hadoop疑难杂症
- java同步:synchronized
- if和switch
- SpringMVC的数据格式化
- C# 根据Word模板,填写内容2
- ORA-12514 TNS 监听程序当前无法识别连接描述符中请求服务
- 1013.数素数
- AB测试
- tomcat的访问权限
- HDU6023-Automatic Judge
- Android中为什么主线程不会因为Looper.loop()里的死循环卡死
- 机房重构--结账存储过程