- 浏览: 21522244 次
- 性别:
博客专栏
-
跟我学spring3
浏览量:2422020
-
Spring杂谈
浏览量:3011677
-
跟开涛学SpringMVC...
浏览量:5641445
-
Servlet3.1规范翻...
浏览量:260474
-
springmvc杂谈
浏览量:1598449
-
hibernate杂谈
浏览量:250504
-
跟我学Shiro
浏览量:5862036
-
跟我学Nginx+Lua开...
浏览量:703367
-
亿级流量网站架构核心技术
浏览量:786351
文章分类
- 全部博客 (329)
- 跟我学Nginx+Lua开发 (13)
- 跟我学spring (54)
- 跟开涛学SpringMVC (34)
- spring4 (16)
- spring杂谈 (50)
- springmvc杂谈 (22)
- 跟我学Shiro (26)
- shiro杂谈 (3)
- hibernate杂谈 (10)
- java开发常见问题分析 (36)
- 加速Java应用开发 (5)
- Servlet 3.1规范[翻译] (21)
- servlet3.x (2)
- websocket协议[翻译] (14)
- websocket规范[翻译] (1)
- java web (6)
- db (1)
- js & jquery & bootstrap (4)
- 非技术 (4)
- reminder[转载] (23)
- 跟叶子学把妹 (8)
- nginx (2)
- 架构 (19)
- flume架构与源码分析 (4)
最新评论
-
xxx不是你可以惹得:
认真看错误代码,有时候重启电脑就行了 醉了 我把数据库配置写死 ...
第十六章 综合实例——《跟我学Shiro》 -
dagger9527:
holyselina 写道您前面说到能获取调用是的参数数组,我 ...
【第六章】 AOP 之 6.6 通知参数 ——跟我学spring3 -
xxx不是你可以惹得:
Access denied for user 'root'@' ...
第十六章 综合实例——《跟我学Shiro》 -
dagger9527:
只有@AspectJ支持命名切入点,而Schema风格不支持命 ...
【第六章】 AOP 之 6.5 AspectJ切入点语法详解 ——跟我学spring3 -
dagger9527:
支持虽然会迟到,但永远不会缺席!
【第四章】 资源 之 4.3 访问Resource ——跟我学spring3
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" }
此处使用DefaultFormattingConversionService的addFormatterForFieldAnnotation注册自定义的注解格式化工厂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
数据会正确的绑定到我们的phoneNumber和date上,即请求的参数能被正确的解析并绑定到我们的参数上。
控制器代码位于cn.javass.chapter7.web.controller.DataFormatTestController中。
如果我们请求参数数据不能被正确解析并绑定或输入的数据不合法等该怎么处理呢?接下来的一节我们来学习下绑定失败处理和数据验证相关知识。
评论
<form:input path="person.birthday"/>
后台person中的字段birthday使用注解DateFormatter(pattern="yyyy-MM-dd")
那么前台传到后台是能够正确的转换成Date的,后台传到前台怎么样格式化呢?
比如一个修改的功能,回显和form表达都用<form:input path="person.birthday"/>这个标签,有办法做吗?
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: ""
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个小时了,这个是时区还是什么问题,该怎么解决?!谢谢
我们可以看到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
我们可以看到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);
我们可以看到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点解释的不明确。
我明白你的意思了
你如果想接错误,需要在model后加一个BindingResult,所有错误信息封装到这
public String test01(@ModelAttribute("formatterModel") FormatterModel model, BindingResult result)
页面就可以使用 form:errors显示错误信息了
如果同时出现CustomDateInitializer(实现WebBindingInitializer接口)日期转换,然后还有@DatetimeFormat,怎么调整它俩的order,让@DatetimeFormat先执行,没有@DatetimeFormat时候,再执行CustomDateInitializer。
这个还真没有研究过,不过我记得是先PropertyEditor 再Converter, Converter时先默认的再自定义的,按照注册顺序执行,无法指定order。
如果你需要自定义还是使用如
http://jinnianshilongnian.iteye.com/blog/1888474
registerCustomEditor 注册
好吧,先谢谢了。
如果同时出现CustomDateInitializer(实现WebBindingInitializer接口)日期转换,然后还有@DatetimeFormat,怎么调整它俩的order,让@DatetimeFormat先执行,没有@DatetimeFormat时候,再执行CustomDateInitializer。
这个还真没有研究过,不过我记得是先PropertyEditor 再Converter, Converter时先默认的再自定义的,按照注册顺序执行,无法指定order。
如果你需要自定义还是使用如
http://jinnianshilongnian.iteye.com/blog/1888474
registerCustomEditor 注册
如果同时出现CustomDateInitializer(实现WebBindingInitializer接口)日期转换,然后还有@DatetimeFormat,怎么调整它俩的order,让@DatetimeFormat先执行,没有@DatetimeFormat时候,再执行CustomDateInitializer。
我测试没有问题的
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"; }
然后其他的就没了,是不是缺少验证类的配置 ?
我没有参考你源码中的示例,基本都是通过博客的描述,自己写的例子
如果控制台有错误 把错误发给我 我看看 或者你把项目发我吧 我看看
我测试没有问题的
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"; }
然后其他的就没了,是不是缺少验证类的配置 ?
我没有参考你源码中的示例,基本都是通过博客的描述,自己写的例子
我测试没有问题的
http://localhost:10080/springmvc-chapter7/format1?totalCount=5555555&discount=0.51&sumMoney=100000.128&phoneNumber=010-12345678
如果方便把你的测试发我 我帮你看看么问题
后来的这几种方式 虽然后台的异常信息已经抛出,但是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异常,这两者之间都是对数据的格式化操作,区别是什么呢。。。
后来的这几种方式 虽然后台的异常信息已经抛出,但是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注解来完成。
后来的这几种方式 虽然后台的异常信息已经抛出,但是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,为不是在异常信息中显示呢。。。这几个数据验证格式化的章节看着很迷糊。。。弄不明白。。。
后来的这几种方式 虽然后台的异常信息已经抛出,但是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>
请问!
1.而且注解只能在parse时候起作用,print的时候不起作用。
是吗?
2.哪如果要写个输出时候的格式化注解怎么实现?
使用spring:eval输出即可
如
<td><spring:eval expression="m.createDate"/></td>
发表评论
-
跟我学SpringMVC目录汇总贴、PDF下载、源码下载
2012-12-22 08:05 621027扫一扫,关注我的公众号 购买地址 ... -
源代码下载——第七章 注解式控制器的数据验证、类型转换及格式化
2012-12-01 07:12 33490源代码请到附件中下载。 其他下载: ... -
SpringMVC数据验证——第七章 注解式控制器的数据验证、类型转换及格式化——跟着开涛学SpringMVC
2012-11-23 07:47 2390547.4、数据验证 7.4.1 ... -
SpringMVC数据格式化——第七章 注解式控制器的数据验证、类型转换及格式化——跟着开涛学SpringMVC
2012-11-19 16:41 46支持一下博主:------------------ ... -
SpringMVC数据类型转换——第七章 注解式控制器的数据验证、类型转换及格式化——跟着开涛学SpringMVC
2012-11-12 20:08 1233917.1、简介 在编写可 ... -
扩展SpringMVC以支持绑定JSON格式的请求参数
2012-11-08 07:43 126091上一篇:《扩展SpringMVC以支持更精准的数据绑 ... -
扩展SpringMVC以支持更精准的数据绑定1
2012-11-06 07:38 76806最新版请点击查看FormM ... -
SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解——跟着开涛学SpringMVC
2012-10-26 07:57 1529026.6.2、@RequestParam绑定单个请求参 ... -
SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解——跟着开涛学SpringMVC
2012-10-23 14:45 15---------------------------- ... -
SpringMVC强大的数据绑定(1)——第六章 注解式控制器详解——跟着开涛学SpringMVC
2012-10-16 07:48 121104到目前为止,请求已经能交给我们的处理器进行处理了,接下来 ... -
Spring MVC 3.1新特性 生产者、消费者请求限定 —— 第六章 注解式控制器详解——跟着开涛学SpringMVC
2012-10-10 14:16 485146.6.5、生产者、消费者限定 6.6.5.1、基 ... -
SpringMVC3强大的请求映射规则详解 第六章 注解式控制器详解——跟着开涛学SpringMVC
2012-09-24 15:29 104914声明:本系列都是原创内容,觉得好就顶一个,让更多人知道! ... -
请求映射之URL路径映射 第六章 注解式控制器详解——跟着开涛学SpringMVC
2012-09-24 11:50 946.5.1.1、普通URL路径映射 @Request ... -
请求映射之请求方法映射限定 第六章 注解式控制器详解——跟着开涛学SpringMVC
2012-09-24 11:48 536.5.2、请求方法映射 ... -
请求映射之请求方法映射限定 第六章 注解式控制器详解——跟着开涛学SpringMVC
2012-09-24 10:47 216.5.2、请求方法映射限定 一般我们熟悉的表单 ... -
请求映射之URL路径映射 第六章 注解式控制器详解——跟着开涛学SpringMVC
2012-09-24 07:05 1636.5.1.1、普通URL路径映射 @Reques ... -
源代码下载 第六章 注解式控制器详解
2012-09-22 07:11 42072源代码请到附件中下载。 其他下载: 跟着 ... -
注解式控制器运行流程及处理器定义 第六章 注解式控制器详解——跟着开涛学SpringMVC
2012-09-20 17:47 67463声明:本系列都是原创内容,觉得好就顶一个,让更多人知道! ... -
注解式控制器运行流程及处理器定义 第六章 注解式控制器详解——跟着开涛学SpringMVC
2012-09-20 15:54 96.1、注解式控制器简介 一、Spring2. ... -
源代码下载 第五章 处理器拦截器详解——跟着开涛学SpringMVC
2012-09-17 07:34 33694源代码请到附件中下 ...
相关推荐
内容概要:本文档阐述了 Python 爬虫的基本概念,详细讲解了构建一个简单有效的爬虫所需的五个关键步骤——确定目标、发送请求、解析内容、提取数据和保存结果,并附带了每个阶段相应的实例代码段帮助理解和实施。此外,文中强调了在网络爬虫过程中必须考虑的因素,如遵纪守法,确保不会干扰到网站正常运作或是违反站点设定的规定;以及提供了实用建议以保障高效而不失礼貌地收集所需信息。最后,文档提及Python爬虫主要应用场景包括但不限于数据挖掘、市场调研以及竞争情报搜集等方面。 适用人群:对于有兴趣了解或深入研究Web Scraping的学生、开发者和技术爱好者。 使用场景及目标:无论是初学者希望获得有关Python编程的第一手经验,还是已经有一定经验的技术工作者打算提高效率解决实际问题都非常合适。 其他说明:本文不仅介绍了基础概念与常用方法论,而且给出了完整的学习路径指导和编程指南,旨在让每位参与者都可以顺利入门并逐步掌握高级技巧。同时提醒使用者务必注意合法合规地运用此类技能。
珠宝门店营销管理导购绩效考核表
员工晋升管理规定
人力资源+大数据+薪酬报告+涨薪调薪,在学习、工作生活中,越来越多的事务都会使用到报告,通常情况下,报告的内容含量大、篇幅较长。那么什么样的薪酬报告才是有效的呢?以下是小编精心整理的调薪申请报告,欢迎大家分享。相信老板看到这样的报告,一定会考虑涨薪的哦。
绩效考核方案(医药公司)
Python源码实例07之对数据分析时判断只能选择Excel或者CSV文件.zip
1、文件内容:alsa-plugins-samplerate-1.1.6-1.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/alsa-plugins-samplerate-1.1.6-1.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装
基于OPC UA Client的数据读取与多途径数据传输:Socket通信、数据库保存及OPC DA SERVER转换,使用OPC UA Client读取服务器的数据,可以使用Socket对外提供数据,可以保存到数据库,可以转为OPC DA SERVER对外提供数据。 ,核心关键词:OPC UA Client; 读取服务器数据; Socket通信; 数据保存; 数据库; 转换为OPC DA SERVER。,"OPC UA Client数据读取与转换:Socket通信、数据库保存及OPC DA SERVER输出"
Python源码实例09之如何使用tkinter模块弹出不同种类的消息提示框.zip
JavaScript 实现钢琴特效(打开HTML即可看到效果)
陇顶728用户好评反馈合集
新能源公司绩效考核管理制度
基于python机器学习9种模型实现心脏病发病预测源码+心脏病数据集+详细注释(高分比赛项目) 【项目介绍】 模型构建与训练:运用 9 种不同的模型架构进行搭建,这些模型可能包括常见的机器学习模型如决策树、随机森林、支持向量机等,也可能涉及深度学习模型如神经网络。针对每种模型,使用预处理后的数据进行训练,通过调整模型的参数(如决策树的深度、神经网络的层数和节点数等),利用合适的损失函数和优化算法(如梯度下降法),使模型能够学习到数据中的模式和规律,尽可能准确地拟合数据,以提高预测的准确性。 模型评估与选择:采用多种评估指标(如准确率、召回率、F1 值、AUC - ROC 曲线等)对训练好的 9 种模型进行评估。比较不同模型在这些指标上的表现,分析每个模型的优势和劣势,从而选择出在预测心脏病方面性能最优的模型,或者根据实际应用场景综合考虑多个模型的组合,以达到更好的预测效果。 预测与应用:将经过评估和选择后的模型应用于新的患者数据,对患者是否患有心脏病进行预测。输出预测结果,并可能提供相应的风险等级或概率估计,为医生的临床诊断提供辅助参考,帮助医生更高效、准确地判断患者的病情,制定合理的
IT项目绩效管理
三电平NPC型APF模型预测控制开关频率优化研究:滞环控制模块应用及效果分析,降低开关频率的三电平npc型APF的模型预测控制。 同等参数下传统的模型预测控制的开关频率大概在4392Hz附近,经过添加滞环控制模块后,开关频率降到了3242Hz,效果显著。 ,核心关键词:降低开关频率; 三电平NPC型APF; 模型预测控制; 滞环控制模块; 开关频率下降。,"优化三电平NPC型APF:模型预测控制降低开关频率至3242Hz"
杨鹏程24年度工作总结及规划
ComfyUI工作流文件和开发API文件 更多内容可以查阅 工作流讲解,文件和文件汇总: 《ComfyUI工作流教程、软件使用、开发指导、模型下载》https://datayang.blog.csdn.net/article/details/145220524 图形桌面工具使用教程: 《我的AI工具箱Tauri+Django环境开发,支持局域网使用》https://datayang.blog.csdn.net/article/details/141897682
Python源码实例01之如何以当前日期批量创建文件.zip
ComfyUI工作流文件和开发API文件 更多内容可以查阅 工作流讲解,文件和文件汇总: 《ComfyUI工作流教程、软件使用、开发指导、模型下载》https://datayang.blog.csdn.net/article/details/145220524 图形桌面工具使用教程: 《我的AI工具箱Tauri+Django环境开发,支持局域网使用》https://datayang.blog.csdn.net/article/details/141897682
C#实现西门子PLC数据通信读写:集成OPC、Socket与数据库的技术实践,C#读写西门子PLC.OPC.数据库.Socket 1、PLC数据通信读写; 2、联合OPC; 3、联合Socket; 4、联合数据库; ,核心关键词:C#读写; PLC数据通信读写; 联合OPC; 联合Socket; 联合数据库。,"C#实现PLC与数据库通信:OPC与Socket联合应用"