`
jinnianshilongnian
  • 浏览: 21431926 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2404633
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:2997324
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5631174
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:257473
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1593044
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:248922
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5847149
Group-logo
跟我学Nginx+Lua开...
浏览量:697994
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:780320
社区版块
存档分类
最新评论

SpringMVC数据格式化——第七章 注解式控制器的数据验证、类型转换及格式化——跟着开涛学SpringMVC

 
阅读更多

 

7.3、数据格式化

在如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等。

 

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

 

7.3.1、架构

1、格式化转换器:提供格式化转换的实现支持。


 

一共有如下两组四个接口:

1、Printer接口:格式化显示接口,将T类型的对象根据Locale信息以某种格式进行打印显示(即返回字符串形式);

package org.springframework.format;
public interface Printer<T> {
	String print(T object, Locale locale); 
}

2、Parser接口:解析接口,根据Locale信息解析字符串到T类型的对象;

package org.springframework.format;
public interface Parser<T> {
	T parse(String text, Locale locale) throws ParseException;
}

解析失败可以抛出java.text.ParseException或IllegalArgumentException异常即可。

 

3、Formatter接口:格式化SPI接口,继承Printer和Parser接口,完成T类型对象的格式化和解析功能;

package org.springframework.format;
public interface Formatter<T> extends Printer<T>, Parser<T> {
}

4、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进行格式化和解析。

 

2、格式化转换器注册器、格式化服务:提供类型转换器注册支持,运行时类型转换API支持。

 

 


一个有如下两种接口:

1、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);
} 

2、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类似)。

 

7.3.2、Spring内建的格式化转换器如下所示:

类名

说明

DateFormatter

java.util.Date<---->String

实现日期的格式化/解析

NumberFormatter

java.lang.Number<---->String

实现通用样式的格式化/解析

CurrencyFormatter

java.lang.BigDecimal<---->String

实现货币样式的格式化/解析

PercentFormatter

java.lang.Number<---->String

实现百分数样式的格式化/解析

NumberFormatAnnotationFormatterFactory

@NumberFormat注解类型的数字字段类型<---->String

①通过@NumberFormat指定格式化/解析格式

②可以格式化/解析的数字类型:Short、Integer、Long、Float、Double、BigDecimal、BigInteger

JodaDateTimeFormatAnnotationFormatterFactory

@DateTimeFormat注解类型的日期字段类型<---->String

①通过@DateTimeFormat指定格式化/解析格式

②可以格式化/解析的日期类型:

joda中的日期类型(org.joda.time包中的):LocalDate、LocalDateTime、LocalTime、ReadableInstant

java内置的日期类型:Date、Calendar、Long

 

classpath中必须有Joda-Time类库,否则无法格式化日期类型

NumberFormatAnnotationFormatterFactory和JodaDateTimeFormatAnnotationFormatterFactory(如果classpath提供了Joda-Time类库)在使用格式化服务实现DefaultFormattingConversionService时会自动注册。

 

7.3.3、示例

在示例之前,我们需要到http://joda-time.sourceforge.net/下载Joda-Time类库,本书使用的是joda-time-2.1版本,将如下jar包添加到classpath:

joda-time-2.1.jar

 

 

7.3.3.1、类型级别的解析/格式化

一、直接使用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)信息进行解析/格式化。

 

其他测试用例请参考cn.javass.chapter7.web.controller.support.formatter.InnerFormatterTest的testNumber测试方法和testDate测试方法。

@Test
public void testWithDefaultFormattingConversionService() {
	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);
	Assert.assertEquals("$1,234.13", conversionService.convert(new BigDecimal("1234.128"), String.class));
	LocaleContextHolder.setLocale(null);
	
        LocaleContextHolder.setLocale(Locale.CHINA);
        Assert.assertEquals("¥1,234.13", conversionService.convert(new BigDecimal("1234.128"), String.class));
	Assert.assertEquals(new BigDecimal("1234.13"), conversionService.convert("¥1,234.13", BigDecimal.class));
	LocaleContextHolder.setLocale(null);} 

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根据本地化信息进行解析/格式化;

 

具体测试代码请参考cn.javass.chapter7.web.controller.support.formatter.InnerFormatterTest的testWithDefaultFormattingConversionService测试方法。

 

三、自定义Formatter进行解析/格式化

此处以解析/格式化PhoneNumberModel为例。

 

1、定义Formatter SPI实现 

 

package cn.javass.chapter7.web.controller.support.formatter;
//省略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));
		}
	}
}

 类似于Convert SPI实现,只是此处的相应方法会传入Locale本地化信息,这样可以为不同地区进行解析/格式化数据。

 

2、测试用例:

package cn.javass.chapter7.web.controller.support.formatter;
//省略import
public class CustomerFormatterTest {
	@Test
	public void test() {
		DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
		conversionService.addFormatter(new PhoneNumberFormatter());

		PhoneNumberModel phoneNumber = new PhoneNumberModel("010", "12345678");
		Assert.assertEquals("010-12345678", conversionService.convert(phoneNumber, String.class));
		
		Assert.assertEquals("010", conversionService.convert("010-12345678", PhoneNumberModel.class).getAreaCode());
	}
}

通过PhoneNumberFormatter可以解析String--->PhoneNumberModel和格式化PhoneNumberModel--->String。

 

到此,类型级别的解析/格式化我们就介绍完了,从测试用例可以看出类型级别的是对项目中的整个类型实施相同的解析/格式化逻辑。

 

有的同学可能需要在不同的类的字段实施不同的解析/格式化逻辑,如用户模型类的注册日期字段只需要如“2012-05-02”格式进行解析/格式化即可,而订单模型类的下订单日期字段可能需要如“2012-05-02 20:13:13”格式进行展示。

 

接下来我们学习一下如何进行字段级别的解析/格式化吧。

 

7.3.3.2、字段级别的解析/格式化

一、使用内置的注解进行字段级别的解析/格式化:

1、测试模型类准备:

package cn.javass.chapter7.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
} 

此处我们使用了Spring字段级别解析/格式化的两个内置注解:

 

@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

 

2、测试用例:

@Test
public void test() throws SecurityException, NoSuchFieldException {
	//默认自动注册对@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(2012-1900, 4, 1));
	model.setOrderDate(new Date(2012-1900, 4, 1, 20, 18, 18));
     
	//获取类型信息
	TypeDescriptor descriptor = 
			new TypeDescriptor(FormatterModel.class.getDeclaredField("totalCount"));		
	TypeDescriptor stringDescriptor = TypeDescriptor.valueOf(String.class);
		
	Assert.assertEquals("10,000", conversionService.convert(model.getTotalCount(), descriptor, stringDescriptor));
	Assert.assertEquals(model.getTotalCount(), conversionService.convert("10,000", stringDescriptor, descriptor));

}

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

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

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

 

3、通过为不同的字段指定不同的注解信息进行字段级别的细粒度数据解析/格式化

 

descriptor = new TypeDescriptor(FormatterModel.class.getDeclaredField("registerDate"));
Assert.assertEquals("2012-05-01", conversionService.convert(model.getRegisterDate(), descriptor, stringDescriptor));
Assert.assertEquals(model.getRegisterDate(), conversionService.convert("2012-05-01", stringDescriptor, descriptor));
		
descriptor = new TypeDescriptor(FormatterModel.class.getDeclaredField("orderDate"));
Assert.assertEquals("2012-05-01 20:18:18", conversionService.convert(model.getOrderDate(), descriptor, stringDescriptor));
Assert.assertEquals(model.getOrderDate(), conversionService.convert("2012-05-01 20:18:18", stringDescriptor, descriptor));

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

 

其他测试用例请参考cn.javass.chapter7.web.controller.support.formatter.InnerFieldFormatterTest的test测试方法。

 

二、自定义注解进行字段级别的解析/格式化:

此处以解析/格式化PhoneNumberModel字段为例。

1、定义解析/格式化字段的注解类型:

package cn.javass.chapter7.web.controller.support.formatter;
//省略import
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface PhoneNumber {
}

 (2、实现AnnotationFormatterFactory注解格式化工厂:

package cn.javass.chapter7.web.controller.support.formatter;
//省略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;
	}
} 

AnnotationFormatterFactory实现会根据注解信息和字段类型获取相应的解析器/格式化器。

 

3、修改FormatterModel添加如下代码:

@PhoneNumber
private PhoneNumberModel phoneNumber; 

4、测试用例

 @Test
public void test() throws SecurityException, NoSuchFieldException {
	DefaultFormattingConversionService conversionService = 
										new DefaultFormattingConversionService();//创建格式化服务
	conversionService.addFormatterForFieldAnnotation(
					new PhoneNumberFormatAnnotationFormatterFactory());//添加自定义的注解格式化工厂
		
	FormatterModel model = new FormatterModel();
	TypeDescriptor descriptor = 
			new TypeDescriptor(FormatterModel.class.getDeclaredField("phoneNumber"));
	TypeDescriptor stringDescriptor = TypeDescriptor.valueOf(String.class);
	
	PhoneNumberModel value = (PhoneNumberModel) conversionService.convert("010-12345678", stringDescriptor, descriptor); //解析字符串"010-12345678"--> PhoneNumberModel
	model.setPhoneNumber(value);
		
	Assert.assertEquals("010-12345678", conversionService.convert(model.getPhoneNumber(), descriptor, stringDescriptor));//格式化PhoneNumberModel-->"010-12345678"
} 

 

此处使用DefaultFormattingConversionServiceaddFormatterForFieldAnnotation注册自定义的注解格式化工厂PhoneNumberFormatAnnotationFormatterFactory

 

到此,编程进行数据的格式化/解析我们就完成了,使用起来还是比较麻烦,接下来我们将其集成到Spring Web MVC环境中。

7.3.4、集成到Spring Web MVC环境

一、注册FormattingConversionService实现和自定义格式化转换器:

 

<bean id="conversionService" 
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
  <!—此处省略之前注册的自定义类型转换器-->
  <property name="formatters">
      <list>
 	      <bean class="cn.javass.chapter7.web.controller.support.formatter.
	       	       	       	       	       	      PhoneNumberFormatAnnotationFormatterFactory"/>
      </list>
  </property>
</bean>

 其他配置和之前学习7.2.2.4一节一样。

 

二、示例:

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>

 

在浏览器输入测试URL:

http://localhost:9080/springmvc-chapter7/format1?totalCount=100000&discount=0.51&sumMoney=100000.128&phoneNumber=010-12345678

 

数据会正确绑定到我们的formatModel,即请求参数能被正确的解析并绑定到我们的命令对象上,而且在JSP页面也能正确的显示格式化后的数据(即正确的被格式化显示)。

 

2、功能处理方法参数级别的数据解析:

@RequestMapping(value = "/format2")
public String test2(
	    @PhoneNumber @RequestParam("phoneNumber") PhoneNumberModel phoneNumber, 
	    @DateTimeFormat(pattern="yyyy-MM-dd") @RequestParam("date") Date date) {
    	System.out.println(phoneNumber);
    	System.out.println(date);
    	return "format/success2";
} 

此处我们可以直接在功能处理方法的参数上使用格式化注解类型进行注解,Spring Web MVC能根据此注解信息对请求参数进行解析并正确的绑定。

 

在浏览器输入测试URL:

http://localhost:9080/springmvc-chapter7/format2?phoneNumber=010-12345678&date=2012-05-01

 

数据会正确的绑定到我们的phoneNumberdate上,即请求的参数能被正确的解析并绑定到我们的参数上。

 

控制器代码位于cn.javass.chapter7.web.controller.DataFormatTestController中。

 

如果我们请求参数数据不能被正确解析并绑定或输入的数据不合法等该怎么处理呢?接下来的一节我们来学习下绑定失败处理和数据验证相关知识。  

 

 

30
0
分享到:
评论
36 楼 jiangyeqt 2015-09-16  
有个问题我纠结了一阵子了
<form:input path="person.birthday"/>

后台person中的字段birthday使用注解DateFormatter(pattern="yyyy-MM-dd")
那么前台传到后台是能够正确的转换成Date的,后台传到前台怎么样格式化呢?
比如一个修改的功能,回显和form表达都用<form:input path="person.birthday"/>这个标签,有办法做吗?
35 楼 dreamsea530 2015-08-20  
    public void edit(HttpServletRequest request, HttpServletResponse response, User user)
user中有int属性为空,就报错怎么处理,O(∩_∩)O谢谢
Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'downAmount'; nested exception is java.lang.NumberFormatException: For input string: ""
34 楼 lidefeng_1112 2015-01-08  
注解用在getter方法不管用?没有相应的TypeDescriptor构造函数。
33 楼 867530559 2014-10-24  
乱。。。。。。。。。。。。。。。。。
32 楼 TheMatrix 2014-07-26  
掏哥(那个tao不允许打:),关于日期转换(javascript json -> java bean的Date)

bean的日期是那么配的:
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")

页面debug js脚本发现值为:2014-7-26 00:25:31

而到了java controller,发现bean中的日期却是:2014-7-26 08:25:31

+8个小时了,这个是时区还是什么问题,该怎么解决?!谢谢
31 楼 jinnianshilongnian 2013-12-11  
neptune 写道
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的实现进行解析。

这个解释还是有些问题的,本质上还是用converter包装了formatter,用sourceType和targetType最为key,进行缓存,需要转换时根据sourceType和targetType得到converter调用convert进行转换的。因此第1点和第2点解释的不明确。


public ParserConverter(java.lang.Class<?> fieldType, org.springframework.format.Parser<?> parser, org.springframework.core.convert.ConversionService conversionService) {}

public PrinterConverter(java.lang.Class<?> fieldType, org.springframework.format.Printer<?> printer, org.springframework.core.convert.ConversionService conversionService) { }

可以看到 其实这两个converter 只关心Parser/Printer
30 楼 jinnianshilongnian 2013-12-11  
neptune 写道
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的实现进行解析。

这个解释还是有些问题的,本质上还是用converter包装了formatter,用sourceType和targetType最为key,进行缓存,需要转换时根据sourceType和targetType得到converter调用convert进行转换的。因此第1点和第2点解释的不明确。


本质上还是用converter包装了formatter: 这里要注意的是Formatter继承自Printer和Parser;而比如PrinterConverter直接找Printer(不一定是Formatter,但是一般我们实现时可能是Formatter);
29 楼 neptune 2013-12-11  
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的实现进行解析。

这个解释还是有些问题的,本质上还是用converter包装了formatter,用sourceType和targetType最为key,进行缓存,需要转换时根据sourceType和targetType得到converter调用convert进行转换的。因此第1点和第2点解释的不明确。
28 楼 jinnianshilongnian 2013-06-20  
回复 Motte2010 :

我明白你的意思了

你如果想接错误,需要在model后加一个BindingResult,所有错误信息封装到这
public String test01(@ModelAttribute("formatterModel") FormatterModel model, BindingResult result)

页面就可以使用 form:errors显示错误信息了
27 楼 aokunsang 2013-06-19  
jinnianshilongnian 写道
aokunsang 写道
你好,请问个问题。
如果同时出现CustomDateInitializer(实现WebBindingInitializer接口)日期转换,然后还有@DatetimeFormat,怎么调整它俩的order,让@DatetimeFormat先执行,没有@DatetimeFormat时候,再执行CustomDateInitializer。

这个还真没有研究过,不过我记得是先PropertyEditor 再Converter, Converter时先默认的再自定义的,按照注册顺序执行,无法指定order。

如果你需要自定义还是使用如
http://jinnianshilongnian.iteye.com/blog/1888474

registerCustomEditor 注册

好吧,先谢谢了。
26 楼 jinnianshilongnian 2013-06-19  
aokunsang 写道
你好,请问个问题。
如果同时出现CustomDateInitializer(实现WebBindingInitializer接口)日期转换,然后还有@DatetimeFormat,怎么调整它俩的order,让@DatetimeFormat先执行,没有@DatetimeFormat时候,再执行CustomDateInitializer。

这个还真没有研究过,不过我记得是先PropertyEditor 再Converter, Converter时先默认的再自定义的,按照注册顺序执行,无法指定order。

如果你需要自定义还是使用如
http://jinnianshilongnian.iteye.com/blog/1888474

registerCustomEditor 注册
25 楼 aokunsang 2013-06-19  
你好,请问个问题。
如果同时出现CustomDateInitializer(实现WebBindingInitializer接口)日期转换,然后还有@DatetimeFormat,怎么调整它俩的order,让@DatetimeFormat先执行,没有@DatetimeFormat时候,再执行CustomDateInitializer。
24 楼 jinnianshilongnian 2013-06-19  
Motte2010 写道
jinnianshilongnian 写道
回复 Motte2010

我测试没有问题的
http://localhost:10080/springmvc-chapter7/format1?totalCount=5555555&discount=0.51&sumMoney=100000.128&phoneNumber=010-12345678

如果方便把你的测试发我 我帮你看看么问题

我的测试除了之前发的model 和 页面外 还有就是*-servlet.xml的配置中 有相关注解的配置 InternalResourceViewResolver ,RequestMappingHandlerMapping,RequestMappingHandlerAdapter 还有一个自定义的FormatterTestcontroller控制器,
控制器方法 :
@RequestMapping(value = "/test1")
	public String test1(@ModelAttribute("formatModel") FormatterModel formatModel,Errors errors){
		System.out.println(formatModel);
		return "format/register";
		
	}

然后其他的就没了,是不是缺少验证类的配置 ?
我没有参考你源码中的示例,基本都是通过博客的描述,自己写的例子

如果控制台有错误 把错误发给我 我看看  或者你把项目发我吧  我看看
23 楼 Motte2010 2013-06-19  
jinnianshilongnian 写道
回复 Motte2010

我测试没有问题的
http://localhost:10080/springmvc-chapter7/format1?totalCount=5555555&discount=0.51&sumMoney=100000.128&phoneNumber=010-12345678

如果方便把你的测试发我 我帮你看看么问题

我的测试除了之前发的model 和 页面外 还有就是*-servlet.xml的配置中 有相关注解的配置 InternalResourceViewResolver ,RequestMappingHandlerMapping,RequestMappingHandlerAdapter 还有一个自定义的FormatterTestcontroller控制器,
控制器方法 :
@RequestMapping(value = "/test1")
	public String test1(@ModelAttribute("formatModel") FormatterModel formatModel,Errors errors){
		System.out.println(formatModel);
		return "format/register";
		
	}

然后其他的就没了,是不是缺少验证类的配置 ?
我没有参考你源码中的示例,基本都是通过博客的描述,自己写的例子
22 楼 jinnianshilongnian 2013-06-19  
回复 Motte2010

我测试没有问题的
http://localhost:10080/springmvc-chapter7/format1?totalCount=5555555&discount=0.51&sumMoney=100000.128&phoneNumber=010-12345678

如果方便把你的测试发我 我帮你看看么问题
21 楼 Motte2010 2013-06-19  
jinnianshilongnian 写道
Motte2010 写道
Motte2010 写道
为什么这个验证的信息,我只有在之前使用WebBindingInitializer的时候,使用form:error标签 ,才能取到错误信息呢?
后来的这几种方式 虽然后台的异常信息已经抛出,但是form:error没任何内容
你之前有说过把错误信息添加到Error  这个怎么添加? 貌似文章中没有说明 ?
我的测试很简单  直接就是写了一个FormatterModel类
@NumberFormat(style = Style.NUMBER,pattern="#,###")
	private int totalCount;
	@NumberFormat(style = Style.PERCENT)
	private double discount;
	@NumberFormat(style = Style.CURRENCY)
	private double totalMoney;
	@DateTimeFormat(iso = ISO.DATE)
	private Date registerDate;
	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	private Date orderDate;

//页面
<form:form commandName="formatModel">
	<form:errors path="*"></form:errors><br/>
		totalCount<form:input path="totalCount"/><br/>
		discount<form:input path="discount"/><br/>
		totalMoney<form:input path="totalMoney"/><br/>
		registerDate<form:input path="registerDate"/><br/>
		orderDate<form:input path="orderDate"/><br/>
		<input type="submit" value="提交"/>
	</form:form>


还有就是,我在页面上 比如totalCount中输入 5555555  为什么是页面直接报500,为不是在异常信息中显示呢。。。这几个数据验证格式化的章节看着很迷糊。。。弄不明白。。。



因为你的格式不对(你需要的是 #,###),如果你需要如错误信息,你需要加如JSR-303注解来完成。

谢谢你的耐心回复,我记得之前第一次讲,使用继承PropertyEditorSupport实现格式化(setAsText)的时候,即使页面上我们输入错误的格式,异常会显示在页面的错误信息中,而上面的这种方式是直接页面抛出500异常,这两者之间都是对数据的格式化操作,区别是什么呢。。。
20 楼 jinnianshilongnian 2013-06-18  
Motte2010 写道
Motte2010 写道
为什么这个验证的信息,我只有在之前使用WebBindingInitializer的时候,使用form:error标签 ,才能取到错误信息呢?
后来的这几种方式 虽然后台的异常信息已经抛出,但是form:error没任何内容
你之前有说过把错误信息添加到Error  这个怎么添加? 貌似文章中没有说明 ?
我的测试很简单  直接就是写了一个FormatterModel类
@NumberFormat(style = Style.NUMBER,pattern="#,###")
	private int totalCount;
	@NumberFormat(style = Style.PERCENT)
	private double discount;
	@NumberFormat(style = Style.CURRENCY)
	private double totalMoney;
	@DateTimeFormat(iso = ISO.DATE)
	private Date registerDate;
	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	private Date orderDate;

//页面
<form:form commandName="formatModel">
	<form:errors path="*"></form:errors><br/>
		totalCount<form:input path="totalCount"/><br/>
		discount<form:input path="discount"/><br/>
		totalMoney<form:input path="totalMoney"/><br/>
		registerDate<form:input path="registerDate"/><br/>
		orderDate<form:input path="orderDate"/><br/>
		<input type="submit" value="提交"/>
	</form:form>


还有就是,我在页面上 比如totalCount中输入 5555555  为什么是页面直接报500,为不是在异常信息中显示呢。。。这几个数据验证格式化的章节看着很迷糊。。。弄不明白。。。



因为你的格式不对(你需要的是 #,###),如果你需要如错误信息,你需要加如JSR-303注解来完成。
19 楼 Motte2010 2013-06-18  
Motte2010 写道
为什么这个验证的信息,我只有在之前使用WebBindingInitializer的时候,使用form:error标签 ,才能取到错误信息呢?
后来的这几种方式 虽然后台的异常信息已经抛出,但是form:error没任何内容
你之前有说过把错误信息添加到Error  这个怎么添加? 貌似文章中没有说明 ?
我的测试很简单  直接就是写了一个FormatterModel类
@NumberFormat(style = Style.NUMBER,pattern="#,###")
	private int totalCount;
	@NumberFormat(style = Style.PERCENT)
	private double discount;
	@NumberFormat(style = Style.CURRENCY)
	private double totalMoney;
	@DateTimeFormat(iso = ISO.DATE)
	private Date registerDate;
	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	private Date orderDate;

//页面
<form:form commandName="formatModel">
	<form:errors path="*"></form:errors><br/>
		totalCount<form:input path="totalCount"/><br/>
		discount<form:input path="discount"/><br/>
		totalMoney<form:input path="totalMoney"/><br/>
		registerDate<form:input path="registerDate"/><br/>
		orderDate<form:input path="orderDate"/><br/>
		<input type="submit" value="提交"/>
	</form:form>


还有就是,我在页面上 比如totalCount中输入 5555555  为什么是页面直接报500,为不是在异常信息中显示呢。。。这几个数据验证格式化的章节看着很迷糊。。。弄不明白。。。
18 楼 Motte2010 2013-06-18  
为什么这个验证的信息,我只有在之前使用WebBindingInitializer的时候,使用form:error标签 ,才能取到错误信息呢?
后来的这几种方式 虽然后台的异常信息已经抛出,但是form:error没任何内容
你之前有说过把错误信息添加到Error  这个怎么添加? 貌似文章中没有说明 ?
我的测试很简单  直接就是写了一个FormatterModel类
@NumberFormat(style = Style.NUMBER,pattern="#,###")
	private int totalCount;
	@NumberFormat(style = Style.PERCENT)
	private double discount;
	@NumberFormat(style = Style.CURRENCY)
	private double totalMoney;
	@DateTimeFormat(iso = ISO.DATE)
	private Date registerDate;
	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	private Date orderDate;

//页面
<form:form commandName="formatModel">
	<form:errors path="*"></form:errors><br/>
		totalCount<form:input path="totalCount"/><br/>
		discount<form:input path="discount"/><br/>
		totalMoney<form:input path="totalMoney"/><br/>
		registerDate<form:input path="registerDate"/><br/>
		orderDate<form:input path="orderDate"/><br/>
		<input type="submit" value="提交"/>
	</form:form>

17 楼 jinnianshilongnian 2013-05-08  
lwkjob 写道
注解方式@DateFormatter 不支持timeStamp
请问!
1.而且注解只能在parse时候起作用,print的时候不起作用。
是吗?
2.哪如果要写个输出时候的格式化注解怎么实现?


使用spring:eval输出即可

<td><spring:eval expression="m.createDate"/></td>

相关推荐

    springmvc注解式控制器的数据验证、类型转换及格式化 SpringMVC数据验证

    springmvc注解式控制器的数据验证、类型转换及格式化 SpringMVC数据验证 参数传递

    跟我学SpringMVC 教程

    第七章 注解式控制器的数据验证、类型转换及格式化 SpringMVC数据类型转换 第七章 注解式控制器的数据验证、类型转换及格式化 SpringMVC数据格式化 第七章 注解式控制器的数据验证、类型转换及格式化 SpringMVC...

    跟我学SpringMVC

    第七章 注解式控制器的数据验证、类型转换及格式化 SpringMVC数据类型转换 第七章 注解式控制器的数据验证、类型转换及格式化 SpringMVC数据格式化 第七章 注解式控制器的数据验证、类型转换及格式化 SpringMVC数据...

    SpringMvc开涛.rar

    PDF,源代码 开涛学SpringMVC 第一章源代码下载 第二章 Spring MVC入门 源代码下载 第四章 Controller接口控制器详解 源代码下载 第五章 处理器拦截器详解——跟着...第七章 注解式控制器的数据验证、类型转换及格式化

    SpringMVC数据类型转换超详细介绍

    SpringMVC数据类型转换超详细介绍,注解式控制器的数据验证、类型转换及格式化

    SpringMVC学习文档

    SpringMVC入门、Controller接口详解、注解详解、数据验证、类型转换及格式化等等

    SpringMVC示例

    员工信息、RESTRUL_CRUD_添加操作&表单标签、RESTRUL_CRUD_删除操作&处理静态资源、RESTRUL_CRUD_修改操作、自定义类型转换器、annotation-driven配置、InitBinder注解、数据的格式化、JSR 303数据校验、错误消息的...

    Spring MVC通过添加自定义注解格式化数据的方法

    主要给大家介绍了关于Spring MVC通过添加自定义注解格式化数据的方法,文中先对springmvc 自定义注解 以及自定义注解的解析进行了详细的介绍,相信会对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面...

    SpringMVC Employee Demo

    driven配置、InitBinder注解、数据的格式化、JSR303数据校验、错误消息的显示及国际化、Ajax返回JSON、使用HttpMessageConverter、国际化_通过超链接切换中英文、文件上传、自定义的拦截器、拦截器的零Xml配置、异常...

    SpringMVC大威天龙.docx

    SpringMVC是Spring提供的一个强大而灵活的Web框架 借助于注解 SpringMVC提供了几乎是POJO的开发模式 使得控制器的开发和测试更加简单 二 SpringMVC优点 1. 角色划分清晰 2. 分工明确 而且扩展相当灵活 3. 和Spring...

    Spring、SpringMVC和Mybatis框架整合包

    手把手教你整合最优雅SSM框架:SpringMVC + Spring + MyBatis 博客地址:http://blog.csdn.net/qq598535550/article/details/51703190 我们看招聘信息的时候,经常会看到这一点,需要具备SSH框架的技能;而且在大...

    spring杂谈 作者zhang KaiTao

    1. spring杂谈[原创] 1.1 Spring事务处理时自我调用的解决方案及一些实现方式的风险 ...1.32 Spring3 Web MVC下的数据类型转换(第一篇)——《跟我学Spring3 Web MVC》抢先看 1.33 Spring 注入集合类型

    Spring MVC 3.0实战指南.ppt

    4、数据转换、格式化、校验 5、数据模型控制 6、视图及解析器 7、其它 目录: Spring MVC 3.0新特性 Spring MVC框架结构 Spring MVC框架结构 框架的实现者 目录 HTTP请求映射原理 Spring MVC进行映射的依据 通过URL...

    Spring3_MVC注解教程.ppt

    支持REST风格的URL 添加更多注解,可完全注解驱动 引入HTTP输入输出转换器...和数据转换、格式化、验证框架无缝集成 对静态资源处理提供特殊支持 更加灵活的控制器方法签名,可完全独立于Servlet API

    spring-mvc-learn:spring-mvc学习代码源码-mvc source code

    转换器(转换器)和格式化(Formatter) 转炉 实现转换器接口 在spring-mvc的配置文件中添加bean:org.springframework.context.support.ConversionServiceFactoryBean,注册转换器,添加注释驱动的转换服务属性 ...

    spring mvc 3.2 参考文档

    Spring的视图解析也非常灵活,控制器通常负责准备含有数据的model Map并选择一种视图名称,当然,它也可以直接直接写响应流,并完成该请求。视图名称解析可以根据文件扩展名或者Accept头内容类型协商,通过bean的...

    server-api:SERVER-API 是一个gui的web接口管理工具,基于 swagger-ui 的后台API开源项目,采用SpringMvc+Maven3+Jdk1.7+Tomcat7

    api 是一个GUI的WEB接口管理工具,基于 的后台API开源项目,采用SpringMvc+Maven3+Jdk1.7+Tomcat7,该项目的初衷是为了更好的去发展Swagger-ui与SpringMvc的结合,希望能够更加灵活,轻量化的根据后台不同的数据源...

    report 开源的访问层中间件,基于java平台

    可用于接口的调试及性能的展示,以及访问控制层的使用,简化控制层的繁琐代码,简化spring mvc的配置,剥离的请求前的一系列操作,耦合性低,report是请求通道采用责任链模式设计,轻便可控、扩展性强。report的测试...

    SSM入门到精通项目实战(附源码)

    掌握SpringMVC的项目整合配置,@Controller,@RequestMapping,@Resource,@PathVariable,@ResponseBody,@ModelAttribute,@CookieValue,@Transactional等注解的使用,json数据传值,国际化,拦截器,权限控制,...

    java猜数字源码-eweb4j-framework:简单的Java网络框架

    声明式事务、事务模板、事务嵌套 丰富的DAO封装类 多数据源、表关联 简单的 IOC 容器 MVC、ORM、IOC 可控开关 键值对配置文件支持 国际化支持 让我们看看它是如何做到的。 == 1. 独具特色的 RESTful 路由 == 讨厌 _...

Global site tag (gtag.js) - Google Analytics