code test

[align=center;] 类名






[size=10.5pt;]DateFormatter[/size]



[size=10.5pt;]实现日期的格式化/解析[/size]





[size=10.5pt;]java.lang.NumberString[/size]



[size=10.5pt;]CurrencyFormatter[/size]



[size=10.5pt;]实现货币样式的格式化/解析[/size]





[size=10.5pt;]java.lang.NumberString[/size]



[size=10.5pt;]NumberFormatAnnotationFormatterFactory[/size]



[size=10.5pt;]①通过@NumberFormat指定格式化/解析格式[/size]



[size=10.5pt;]JodaDateTimeFormatAnnotationFormatterFactory[/size]



[size=10.5pt;]①通过@DateTimeFormat指定格式化/解析格式[/size]

[size=10.5pt;]joda中的日期类型(org.joda.time包中的):LocalDate、LocalDateTime、LocalTime、ReadableInstant[/size]

[size=10.5pt;] [/size]


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

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



Java代码  [/align]

joda-time-2.1.jar 



7.3.3.1、类型级别的解析/格式化
一、直接使用Formatter SPI进行解析/格式化


Java代码 

//二、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)); 
          


[size=10.5pt;]print[/size][size=10.5pt; color: maroon;]方法:[/size][size=10.5pt;]将[/size][size=10.5pt; font-family: 'Courier New';]BigDecimal[/size][size=10.5pt;]类型数据根据[/size][size=10.5pt;]Locale[/size][size=10.5pt; color: maroon;]信息[/size][size=10.5pt;]格式化为字符串数据进行展示。[/size]

[size=10.5pt;]不同于Convert SPI,Formatter SPI可以根据本地化(Locale)信息进行解析/格式化。[/size]

[size=10.5pt;]其他测试用例请参考cn.javass.chapter7.web.controller.support.formatter.InnerFormatterTest的testNumber测试方法和testDate测试方法。[/size]


Java代码 

@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);}  


[size=10pt;]conversionService.addFormatter()[/size][size=10pt; color: maroon;]:[/size][size=10pt;]注册[/size][size=10pt; font-family: 'Courier New';]Formatter SPI[/size][size=10pt;]实现;[/size]

[size=10pt;]conversionService.convert("[/size][size=10pt; color: maroon;]¥[/size][size=10pt;]1,234.13", BigDecimal. class)[/size][size=10pt; color: maroon;]:[/size][size=10pt;]用于将字符串类型数据解析为[/size][size=10pt; font-family: 'Courier New';]BigDecimal[/size][size=10pt;]类型数据,此处也是根据“[/size][size=10pt; font-family: 'Courier New';]LocaleContextHolder. setLocale(locale)[/size][size=10pt;]”设置的本地化信息进行解;[/size]





[size=10.5pt;]此处以解析/格式化PhoneNumberModel为例。[/size]





Java代码 

package cn.javass.chapter7.web.controller.support.formatter; 
//省略import 
public class PhoneNumberFormatter implements Formatter { 
    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)); 
        } 
    } 


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





Java代码 

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()); 
    } 


[size=10.5pt;]通过PhoneNumberFormatter可以解析String--->PhoneNumberModel和格式化PhoneNumberModel--->String。[/size]

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

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

[size=10.5pt;]接下来我们学习一下如何进行字段级别的解析/格式化吧。[/size]

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



Java代码 

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 
}  

[size=10.5pt;]此处我们使用了Spring字段级别解析/格式化的两个内置注解:[/size]

[size=10.5pt; color: maroon;]@Number:[/size][size=10.5pt;]定义数字相关的解析/格式化元数据(通用样式、货币样式、百分数样式),参数如下:[/size]

[size=10.5pt;]    pattern:[/size]自定义样式,如patter=[size=10pt;]"#,###"[/size][size=10pt;];[/size]

[size=10.5pt; color: maroon;]@[/size][size=10pt;]DateTimeFormat[/size][size=10pt; color: maroon;]:[/size][size=10pt;]定义日期相关的解析[/size][size=10pt;]/[/size][size=10pt;]格式化元数据,参数如下:[/size]

[size=10.5pt; color: maroon;]iso:[/size][size=10.5pt;]指定解析/格式化字段数据的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;[/size]

[size=10.5pt; color: maroon;]优先级: [/size][size=10.5pt;]pattern 大于 iso 大于[/size][size=10pt;] style[/size][size=10pt;]。[/size]

(2、测试用例:



Java代码 

@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)); 
 


[size=10.5pt; font-family: 'Courier New'; color: maroon;]TypeDescriptor[/size][size=10.5pt; color: maroon;]:[/size][size=10.5pt;]拥有类型信息的上下文,用于[/size][size=10.5pt; font-family: 'Courier New';]Spring3[/size][size=10.5pt;]类型转换系统获取类型信息的(可以包含类、字段、方法参数、属性信息);通过[/size][size=10.5pt; font-family: 'Courier New';]TypeDescriptor[/size][size=10.5pt;],我们就可以获取(类、字段、方法参数、属性)的各种信息,如注解类型信息;[/size]

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

[size=10.5pt; font-family: 'Courier New';] [/size]




Java代码 

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)); 

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


[size=10.5pt;]其他测试用例请参考cn.javass.chapter7.web.controller.support.formatter.InnerFieldFormatterTest的test测试方法。[/size]

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

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


Java代码 

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


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




Java代码 

package cn.javass.chapter7.web.controller.support.formatter; 
//省略import 
public class PhoneNumberFormatAnnotationFormatterFactory 
    implements AnnotationFormatterFactory {//①指定可以解析/格式化的字段注解类型 
 
    private final Set> fieldTypes; 
    private final PhoneNumberFormatter formatter; 
    public PhoneNumberFormatAnnotationFormatterFactory() { 
        Set> set = new HashSet>(); 
        set.add(PhoneNumberModel.class); 
        this.fieldTypes = set; 
        this.formatter = new PhoneNumberFormatter();//此处使用之前定义的Formatter实现 
    } 
    //②指定可以被解析/格式化的字段类型集合 
    @Override 
    public Set> getFieldTypes() { 
        return fieldTypes; 
    } 
    //③根据注解信息和字段类型获取解析器 
    @Override 
    public Parser> getParser(PhoneNumber annotation, Class> fieldType) { 
        return formatter; 
    } 
    //④根据注解信息和字段类型获取格式化器 
    @Override    
    public Printer> getPrinter(PhoneNumber annotation, Class> fieldType) { 
        return formatter; 
    } 
}  

[size=10pt; font-family: 'Courier New';]AnnotationFormatterFactory[/size][size=10pt;]实现会根据注解信息和字段类型获取相应的解析器[/size][size=10pt; font-family: 'Courier New';]/[/size][size=10pt;]格式化器。[/size]

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


Java代码 

@PhoneNumber 
private PhoneNumberModel phoneNumber;  




Java代码 

@Test 
ublic 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" 
  


[size=10.5pt;]此处使用[/size][size=10.5pt; font-family: 'Courier New';]DefaultFormattingConversionService[/size][size=10.5pt;]的[/size][size=10.5pt; font-family: 'Courier New';]addFormatterForFieldAnnotation[/size][size=10.5pt;]注册自定义的注解格式化工厂[/size][size=10pt; font-family: 'Courier New';]PhoneNumberFormatAnnotationFormatterFactory[/size][size=10.5pt;]。[/size]

[size=10.5pt;]到此,编程进行数据的格式化/解析我们就完成了,使用起来还是比较麻烦,接下来我们将其集成到Spring Web MVC环境中。[/size]
7.3.4、集成到Spring Web MVC环境
一、注册FormattingConversionService实现和自定义格式化转换器:




Java代码 

"conversionService"  
class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> 
   
  "formatters"> 
     
          class="cn.javass.chapter7.web.controller.support.formatter. 
                                                  PhoneNumberFormatAnnotationFormatterFactory"/> 
       
   
 

[size=10.5pt;]其他配置和之前学习[/size][size=10.5pt;]7.2.2.4一节一样。[/size]


二、示例:




Java代码 

@RequestMapping(value = "/format1") 
public String test1(@ModelAttribute("model") FormatterModel formatModel) { 
    return "format/success"; 
}  



Java代码 

totalCount:"model.totalCount">${status.value}
discount:"model.discount">${status.value}
sumMoney:"model.sumMoney">${status.value}
phoneNumber:"model.phoneNumber">${status.value}
 
phoneNumber:"model.phoneNumber">
 

"model"> 
    "phoneNumber"/>
    "sumMoney"/> 
 


[size=10.5pt;]在浏览器输入测试URL:[/size]

[size=10.5pt;] [/size]

[size=10.5pt;] [/size]



Java代码 

@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"; 
}  

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

[size=10.5pt;]在浏览器输入测试URL:[/size]

[size=10.5pt;] [/size]

[size=10pt; font-family: 'Courier New';] [/size]

[size=10pt; font-family: 'Courier New';] [/size]
[size=10.5pt;]如果我们请求参数数据不能被正确解析并绑定或输入的数据不合法等该怎么处理呢?接下来的一节我们来学习下绑定失败处理和数据验证相关知识。[/size] 




[url=http://jinnianshilongnian.iteye.com/blog/1729739] 25
顶[/url][url=http://jinnianshilongnian.iteye.com/blog/1729739] 0
踩[/url]
分享到: [img]http://jinnianshilongnian.iteye.com/images/sina.jpg" alt="[/img] [img]http://jinnianshilongnian.iteye.com/images/tec.jpg" alt="[/img]


SpringMVC数据验证——第七章 注解式控制 ... | SpringMVC数据类型转换——第七章 注解式 ...



    2012-11-19 19:18
    浏览 50303
    评论(36)
    收藏
    分类:企业架构
    相关推荐




评论


36 楼 jiangyeqt 2015-09-16   引用
有个问题我纠结了一阵子了


后台person中的字段birthday使用注解DateFormatter(pattern="yyyy-MM-dd")
那么前台传到后台是能够正确的转换成Date的,后台传到前台怎么样格式化呢?
比如一个修改的功能,回显和form表达都用这个标签,有办法做吗?


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   引用



猜你喜欢

转载自mrlittlenew.iteye.com/blog/2350757