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

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
分享到:
评论
16 楼 lwkjob 2013-05-08  
注解方式@DateFormatter 不支持timeStamp
请问!
1.而且注解只能在parse时候起作用,print的时候不起作用。
是吗?
2.哪如果要写个输出时候的格式化注解怎么实现?
15 楼 jinnianshilongnian 2013-04-15  
yzsunlight 写道
集成到Spring Web MVC环境章节
测试时,出现了
org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.format.annotation.NumberFormat double for value '0.51'; nested exception is java.lang.IllegalArgumentException: Unable to parse '0.51'


org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.format.annotation.NumberFormat double for value '100000.128'; nested exception is java.lang.IllegalArgumentException: Unable to parse '100000.128'
错误,后台报错了,但页面正常显示了,
我的问题跟3楼的问题一样


DEBUG org.springframework.beans.TypeConverterDelegate - Original ConversionService attempt failed - ignored since PropertyEditor based conversion eventually succeeded 
org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.format.annotation.NumberFormat double for value '100000.128'; nested exception is java.lang.IllegalArgumentException: Unable to parse '100000.128' 

这些是debug信息 不用管
14 楼 jinnianshilongnian 2013-04-15  
yzsunlight 写道
集成到Spring Web MVC环境章节
测试时,出现了
org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.format.annotation.NumberFormat double for value '0.51'; nested exception is java.lang.IllegalArgumentException: Unable to parse '0.51'


org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.format.annotation.NumberFormat double for value '100000.128'; nested exception is java.lang.IllegalArgumentException: Unable to parse '100000.128'
错误,后台报错了,但页面正常显示了,
我的问题跟3楼的问题一样

给下详细的异常吧
13 楼 yzsunlight 2013-04-14  
集成到Spring Web MVC环境章节
测试时,出现了
org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.format.annotation.NumberFormat double for value '0.51'; nested exception is java.lang.IllegalArgumentException: Unable to parse '0.51'


org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.format.annotation.NumberFormat double for value '100000.128'; nested exception is java.lang.IllegalArgumentException: Unable to parse '100000.128'
错误,后台报错了,但页面正常显示了,
我的问题跟3楼的问题一样
12 楼 yzsunlight 2013-04-14  
集成到Spring Web MVC环境章节
测试时,出现了
org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.format.annotation.NumberFormat double for value '0.51'; nested exception is java.lang.IllegalArgumentException: Unable to parse '0.51'


org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.format.annotation.NumberFormat double for value '100000.128'; nested exception is java.lang.IllegalArgumentException: Unable to parse '100000.128'
错误,后台报错了,但页面正常显示了,
我的问题跟3楼的问题一样
11 楼 jinnianshilongnian 2013-03-18  
jpr1990 写道
jpr1990 写道
<form:input path="saleTime" >标签怎样使用格式化标签嵌套:
<fmt:formatDate value="${user.loginDate}" type="both" dateStyle="full"/>

我想格式化初始时间的显示格式:
<form:input path="saleTime"  onfocus="WdatePicker({isShowWeek:true,dateFmt:'yyyy-MM-dd',startDate:'<fmt:formatDate value="${user.loginDate}" type="both" dateStyle="full"/>}')" pattern="yyyy-MM-dd" onClick="WdatePicker()"  htmlEscape="false" maxlength="20" class="Wdate"/>
报错!

求解

报错内容:
系统发生内部错误.

错误信息:

org.apache.jasper.JasperException: /WEB-INF/views/modules/xgg/productForm.jsp(113,126) Unterminated <form:input tag
后台:
013-03-17 20:33:38,460 [http-8080-9] ERROR [500.jsp] - /WEB-INF/views/modules/xgg/productForm.jsp(113,126) Unterminated &lt;form:input tag
org.apache.jasper.JasperException: /WEB-INF/views/modules/xgg/productForm.jsp(113,126) Unterminated &lt;form:input tag



class="Wdate"  ---->cssClass="Wdate"
10 楼 jpr1990 2013-03-17  
jpr1990 写道
<form:input path="saleTime" >标签怎样使用格式化标签嵌套:
<fmt:formatDate value="${user.loginDate}" type="both" dateStyle="full"/>

我想格式化初始时间的显示格式:
<form:input path="saleTime"  onfocus="WdatePicker({isShowWeek:true,dateFmt:'yyyy-MM-dd',startDate:'<fmt:formatDate value="${user.loginDate}" type="both" dateStyle="full"/>}')" pattern="yyyy-MM-dd" onClick="WdatePicker()"  htmlEscape="false" maxlength="20" class="Wdate"/>
报错!

求解

报错内容:
系统发生内部错误.

错误信息:

org.apache.jasper.JasperException: /WEB-INF/views/modules/xgg/productForm.jsp(113,126) Unterminated <form:input tag
后台:
013-03-17 20:33:38,460 [http-8080-9] ERROR [500.jsp] - /WEB-INF/views/modules/xgg/productForm.jsp(113,126) Unterminated &lt;form:input tag
org.apache.jasper.JasperException: /WEB-INF/views/modules/xgg/productForm.jsp(113,126) Unterminated &lt;form:input tag
9 楼 jpr1990 2013-03-17  
<form:input path="saleTime" >标签怎样使用格式化标签嵌套:
<fmt:formatDate value="${user.loginDate}" type="both" dateStyle="full"/>

我想格式化初始时间的显示格式:
<form:input path="saleTime"  onfocus="WdatePicker({isShowWeek:true,dateFmt:'yyyy-MM-dd'},startDate:'<fmt:formatDate value="${user.loginDate}" type="both" dateStyle="full"/>')" pattern="yyyy-MM-dd" onClick="WdatePicker()"  htmlEscape="false" maxlength="20" class="Wdate"/>
报错!

求解
8 楼 jinnianshilongnian 2013-01-08  
yycdaizi 写道
jinnianshilongnian 写道
yycdaizi 写道
您好,向您请教个问题。在用spring mvc 处理ajax请求时,会把返回的对象自动转为json字符串,怎么让这些返回的数据也实现自动格式化呢?
PS:我现在的情况是ajax返回的对象里面有个Date类型的属性,它转为json时是一个数值(毫秒数),我想让根据属性上的@DateTimeFormat注解配置自动转为相应格式的字符串,可以实现吗?

1、你试下@DateTimeFormat
2、使用jackson的json注解
http://blog.csdn.net/yuanyuan110_l/article/details/7000307

用@DateTimeFormat不行,因为返回json的时候根本不会调用FormattingConversionService处理字段。用jackson的注解倒是可以,我现在就是这么解决的,但是这样的话格式化输出要配一个json的注解,而要根据格式自动转换输入则要配@DateTimeFormat注解,要配置2次,感觉不是很统一,不知道这算不算spring mvc的一个缺陷。
另外,使用json注解来格式化还要单写一个类,你想要不同的格式就要多写几个类,用着不是很方便。真希望以后能统一成一个注解!
多谢龙哥的指点了!

你说的是,使用别人的API就是有这种不统一性,希望未来它自己实现JSON API
7 楼 yycdaizi 2013-01-08  
jinnianshilongnian 写道
yycdaizi 写道
您好,向您请教个问题。在用spring mvc 处理ajax请求时,会把返回的对象自动转为json字符串,怎么让这些返回的数据也实现自动格式化呢?
PS:我现在的情况是ajax返回的对象里面有个Date类型的属性,它转为json时是一个数值(毫秒数),我想让根据属性上的@DateTimeFormat注解配置自动转为相应格式的字符串,可以实现吗?

1、你试下@DateTimeFormat
2、使用jackson的json注解
http://blog.csdn.net/yuanyuan110_l/article/details/7000307

用@DateTimeFormat不行,因为返回json的时候根本不会调用FormattingConversionService处理字段。用jackson的注解倒是可以,我现在就是这么解决的,但是这样的话格式化输出要配一个json的注解,而要根据格式自动转换输入则要配@DateTimeFormat注解,要配置2次,感觉不是很统一,不知道这算不算spring mvc的一个缺陷。
另外,使用json注解来格式化还要单写一个类,你想要不同的格式就要多写几个类,用着不是很方便。真希望以后能统一成一个注解!
多谢龙哥的指点了!
6 楼 jinnianshilongnian 2013-01-03  
yycdaizi 写道
您好,向您请教个问题。在用spring mvc 处理ajax请求时,会把返回的对象自动转为json字符串,怎么让这些返回的数据也实现自动格式化呢?
PS:我现在的情况是ajax返回的对象里面有个Date类型的属性,它转为json时是一个数值(毫秒数),我想让根据属性上的@DateTimeFormat注解配置自动转为相应格式的字符串,可以实现吗?

1、你试下@DateTimeFormat
2、使用jackson的json注解
http://blog.csdn.net/yuanyuan110_l/article/details/7000307
5 楼 yycdaizi 2013-01-01  
您好,向您请教个问题。在用spring mvc 处理ajax请求时,会把返回的对象自动转为json字符串,怎么让这些返回的数据也实现自动格式化呢?
PS:我现在的情况是ajax返回的对象里面有个Date类型的属性,它转为json时是一个数值(毫秒数),我想让根据属性上的@DateTimeFormat注解配置自动转为相应格式的字符串,可以实现吗?
4 楼 jinnianshilongnian 2012-11-23  
shyf12054213 写道
org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.format.annotation.NumberFormat double for value '0.51'; nested exception is java.lang.IllegalArgumentException: Unable to parse '0.51'

org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.format.annotation.NumberFormat double for value '100000.128'; nested exception is java.lang.IllegalArgumentException: Unable to parse '100000.128'


看提示像是@NumberFormat(style=Style.PERCENT) 
@NumberFormat(style=Style.CURRENCY) 
这两个注解对应的字段不能解释。

要是方便 可否把源码站内信我 具体看看
3 楼 shyf12054213 2012-11-23  
org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.format.annotation.NumberFormat double for value '0.51'; nested exception is java.lang.IllegalArgumentException: Unable to parse '0.51'

org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type @org.springframework.format.annotation.NumberFormat double for value '100000.128'; nested exception is java.lang.IllegalArgumentException: Unable to parse '100000.128'


看提示像是@NumberFormat(style=Style.PERCENT) 
@NumberFormat(style=Style.CURRENCY) 
这两个注解对应的字段不能解释。
2 楼 jinnianshilongnian 2012-11-23  
shyf12054213 写道
你好。向你请教个问题。按照你的文章我做示例1测试的时候页面能格式化显示,可是后台报错不能准确格式化 @NumberFormat(style=Style.PERCENT) 
6.    private double discount; 
7.    @NumberFormat(style=Style.CURRENCY) 
8.    private double sumMoney;
这两个字段一下是我的配置文档。
<!--Spring3.1开始的注解 HandlerMapping -->
<!--Spring3.1开始的注解 HandlerMapping -->
	<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" >
		<!-- <property name="interceptors">
			<list>
				<bean class="org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor">
					<constructor-arg ref="conversionService"></constructor-arg>
				</bean>
			</list>
		</property> -->
	</bean>
	
	
	<!--Spring3.1开始的注解 HandlerAdapter -->
	<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
		<!-- <property name="webBindingInitializer">
			<bean class="cn.javass.chapter7.web.controller.support.initializer.MyWebBindingInitializer" />
		</property> -->
		<property name="webBindingInitializer" ref="webBindingInitializer"></property>
		<!--线程安全的访问session -->
		<property name="synchronizeOnSession" value="true" />
	</bean>
	<!-- ViewResolver -->
	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="viewClass"
			value="org.springframework.web.servlet.view.JstlView" />
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>
	<!-- ①注册ConversionService -->
	<bean id="conversionService"
		class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!-- 		<property name="converters"> -->
<!-- 			<list> -->
<!-- 				<bean class="cn.javass.chapter7.web.controller.support.converter.StringToPhoneNumberConverter" /> -->
<!-- 				<bean class="cn.javass.chapter7.web.controller.support.converter.StringToDateConverter"> -->
<!-- 					<constructor-arg value="yyyy-MM-dd" /> -->
<!-- 				</bean> -->
<!-- 			</list> -->
<!-- 		</property> -->
		<property name="formatters">
			<list>
				<bean class="cn.javass.chapter7.web.controller.support.formatter.PhoneNumberFormatAnnotationFormatterFactory"></bean>
				<bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory"/>
				<bean class="org.springframework.format.datetime.joda.JodaDateTimeFormatAnnotationFormatterFactory"/>
			</list>
		</property>
	</bean>
	<!-- ②使用ConfigurableWebBindingInitializer注册conversionService -->
	<bean id="webBindingInitializer" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
		<property name="conversionService" ref="conversionService"/>
	</bean>
	<bean class="cn.javass.chapter7.web.controller.DataBinderTestController" />
	<bean class="cn.javass.chapter7.web.controller.FormatController"/>
	<bean class="cn.javass.chapter7.model.FormatterModel"/>

方便把错误贴一下
1 楼 shyf12054213 2012-11-23  
你好。向你请教个问题。按照你的文章我做示例1测试的时候页面能格式化显示,可是后台报错不能准确格式化 @NumberFormat(style=Style.PERCENT) 
6.    private double discount; 
7.    @NumberFormat(style=Style.CURRENCY) 
8.    private double sumMoney;
这两个字段一下是我的配置文档。
<!--Spring3.1开始的注解 HandlerMapping -->
<!--Spring3.1开始的注解 HandlerMapping -->
	<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" >
		<!-- <property name="interceptors">
			<list>
				<bean class="org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor">
					<constructor-arg ref="conversionService"></constructor-arg>
				</bean>
			</list>
		</property> -->
	</bean>
	
	
	<!--Spring3.1开始的注解 HandlerAdapter -->
	<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
		<!-- <property name="webBindingInitializer">
			<bean class="cn.javass.chapter7.web.controller.support.initializer.MyWebBindingInitializer" />
		</property> -->
		<property name="webBindingInitializer" ref="webBindingInitializer"></property>
		<!--线程安全的访问session -->
		<property name="synchronizeOnSession" value="true" />
	</bean>
	<!-- ViewResolver -->
	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="viewClass"
			value="org.springframework.web.servlet.view.JstlView" />
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>
	<!-- ①注册ConversionService -->
	<bean id="conversionService"
		class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!-- 		<property name="converters"> -->
<!-- 			<list> -->
<!-- 				<bean class="cn.javass.chapter7.web.controller.support.converter.StringToPhoneNumberConverter" /> -->
<!-- 				<bean class="cn.javass.chapter7.web.controller.support.converter.StringToDateConverter"> -->
<!-- 					<constructor-arg value="yyyy-MM-dd" /> -->
<!-- 				</bean> -->
<!-- 			</list> -->
<!-- 		</property> -->
		<property name="formatters">
			<list>
				<bean class="cn.javass.chapter7.web.controller.support.formatter.PhoneNumberFormatAnnotationFormatterFactory"></bean>
				<bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory"/>
				<bean class="org.springframework.format.datetime.joda.JodaDateTimeFormatAnnotationFormatterFactory"/>
			</list>
		</property>
	</bean>
	<!-- ②使用ConfigurableWebBindingInitializer注册conversionService -->
	<bean id="webBindingInitializer" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
		<property name="conversionService" ref="conversionService"/>
	</bean>
	<bean class="cn.javass.chapter7.web.controller.DataBinderTestController" />
	<bean class="cn.javass.chapter7.web.controller.FormatController"/>
	<bean class="cn.javass.chapter7.model.FormatterModel"/>

相关推荐

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

    总结来说,Spring MVC的注解式控制器提供了强大的数据验证、类型转换和格式化功能,简化了Web开发过程,提升了应用的安全性和用户体验。通过合理利用这些特性,开发者可以构建更加健壮、易于维护的Web应用。

    SpringMvc开涛.rar

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

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

    SpringMVC在数据类型转换、验证及格式化方面做了大量的改进和完善,为开发者提供了极为方便的工具和支持。通过对Spring 3之后的新特性进行深入理解,可以帮助开发者更好地利用这些功能,提升项目的整体质量。

    跟开涛学SpringMVC(7.2)SpringMVC数据

    总之,"跟开涛学SpringMVC(7.2)SpringMVC数据"课程旨在通过详细讲解Spring MVC的数据处理、格式化和Java开发实践,帮助开发者掌握Web应用程序开发的关键技术,提高他们的开发效率和代码质量。通过学习和实践,学员...

    SpringMVC-8 数据类型转换、数据格式化与数据校验

    在Spring MVC框架中,数据类型转换、数据格式化和数据校验是开发Web应用程序时不可或缺的部分。这些功能有助于确保从客户端接收到的数据准确无误,同时提供了一种优雅的方式来处理和展示这些数据。本篇文章将深入...

    跟我学SpringMVC 教程

    第七章注解式控制器的数据验证、类型转换及格式化则专注于如何在SpringMVC中处理数据验证、类型转换和格式化。这包括了如何使用SpringMVC内建的数据绑定器,将HTTP请求参数绑定到控制器方法的参数上;如何对输入数据...

    Spring MVC学习(七)-------SpringMVC数据类型转换

    在进行Web应用开发时,尤其是在使用Spring MVC框架的过程中,经常需要对用户提交的数据进行处理,包括但不限于数据类型转换、数据验证以及数据格式化等操作。这些操作对于确保数据的准确性和应用程序的健壮性至关...

    SpringMVC数据格式化.docx

    当你在控制器方法的参数中使用 `@RequestParam` 或 `@PathVariable` 注解时,Spring MVC 将自动使用已注册的 `Formatter` 进行解析和格式化。 除了手动实现 `Formatter`,Spring MVC还提供了 `...

    SpringMvc教程 跟着我学SpringMVC

    数据类型转换、数据格式化也是SpringMVC提供的强大功能,这部分内容将教会我们如何进行数据类型的转换和格式化操作。 最后,教程会提供每章的源代码下载链接,帮助学习者通过实践来加深对知识的理解。通过下载和...

    专题资料(2021-2022年)SpringMVC数据类型转换要点.doc

    SpringMVC 数据类型转换是Spring Web MVC框架中的一个重要部分,主要负责将HTTP请求中的字符串数据转换为控制器方法中期望的数据类型。在2021-2022年的专题资料中,这一主题聚焦于Spring 3之后引入的新特性和改进,...

    SSM笔记-SpringMVC的数据格式化 、数据校验、错误提示、错误信息国际化、返回json

    在SpringMVC中,数据格式化是将用户输入的数据转换为业务对象的过程。这通常涉及到日期、货币和其他复杂类型的输入。Spring提供了`@DateTimeFormat`和`@NumberFormat`注解来帮助自动解析和格式化日期和数字。另外,...

    SpringMVC入门很简单之数据验证

    在控制器方法中,可以使用`@Valid`注解来激活验证,如: ```java @PostMapping("/register") public String register(@Valid User user, BindingResult result) { if (result.hasErrors()) { // 处理错误,如...

    注解式springMVC的demo

    在Spring MVC中,注解被广泛用于配置控制器、方法映射、数据绑定等。例如,`@Controller`用于标识一个类作为处理HTTP请求的控制器,而`@RequestMapping`注解则用于将URL路径映射到特定的方法。 2. **@Controller与...

    跟我学SpringMVC

    **第七章 注解式控制器的数据验证、类型转换及格式化** 这部分内容详细讲解了Spring MVC如何处理数据验证,利用JSR-303/JSR-349标准进行Bean验证。同时,探讨了数据类型的自动转换和格式化,包括日期、数字等特殊...

    跟开涛学SpringMVC

    开涛在其开源电子书中《跟开涛学SpringMVC》对SpringMVC进行了详细的介绍,并提供了入门Web开发的学习手册,以其简单易懂的方式阐述了相关知识点。 首先,书中提到了Web开发中的请求-响应模型。在Web世界中,Web...

    SpringMVC中后台转换json格式

    为了解决这个问题,我们需要确保SpringMVC能够正确地将返回的数据转换为JSON格式,并设置合适的响应头。 首先,我们需要引入Jackson库来处理JSON序列化和反序列化。Jackson是Java中广泛使用的JSON处理库,包括三个...

    SpringMVC纯注解配置

    1. **@Controller**:这个注解用于标记一个类作为SpringMVC的控制器。控制器类将处理HTTP请求,并调用业务逻辑方法。 2. **@RequestMapping**:此注解用于映射HTTP请求到特定的方法。可以放在类级别或方法级别,...

Global site tag (gtag.js) - Google Analytics