SpringFramework核心技术三:Spring时间处理和类型转换
使用 在(1)中,我们学习了蛮多的基本概念,在(2)中咱们看一下如何使用的问题。 一、以编程方式使用ConversionService 要以编程方式使用ConversionService实例,只需为其他bean注入一个引用即可: @Service public class MyService { @Autowired public MyService(ConversionService conversionService) { this.conversionService = conversionService; } public void doIt() { this.conversionService.convert(...) } } 对于大多数用例,可以使用convert指定targetType的方法,但它不适用于更复杂的类型,如参数化元素的集合。如果你想转换List的Integer到List的String程序,例如,你需要提供的源和目标类型的正式定义。 幸运的是,TypeDescriptor提供了各种选项来简单明了: DefaultConversionService cs = new DefaultConversionService(); List<Integer> input = .... cs.convert(input, TypeDescriptor.forObject(input), // List<Integer> type descriptor TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class))); 请注意,DefaultConversionService自动注册转换器适用于大多数环境。这包括收集器,标转换器,也基本Object到String转换器。相同的转换器可以ConverterRegistry使用该类 上的静态 addDefaultConverters方法进行注册DefaultConversionService。 值类型转换器将被重新用于数组和集合,所以没有必要创建一个特定的转换器从转换Collection的S到 Collection的T,假设标准收集处理是适当的。 二、Spring字段格式 正如前一节所讨论的,core.convert是一种通用型转换系统。它提供统一的ConversionService API以及用于实现从一种类型到另一种类型的转换逻辑的强类型转换器SPI。Spring容器使用这个系统绑定bean属性值。另外,Spring表达式语言(SpEL)和DataBinder都使用这个系统来绑定字段值。例如,当使用SpEL需要强迫一个Short到一个Long完成的expression.setValue(Object bean, Object value)尝试,core.convert系统执行强制。 现在考虑典型客户端环境(如Web或桌面应用程序)的类型转换要求。在这样的环境中,您通常从String转换 为支持客户端回发过程,并返回String以支持视图呈现过程。另外,您经常需要本地化字符串值。更一般的core.convert Converter SPI没有直接解决这种格式化要求。为了直接解决它们,Spring 3引入了一个方便的Formatter SPI,为客户端环境提供了PropertyEditor的一个简单而强大的替代方案。 通常,在需要实现通用类型转换逻辑时使用Converter SPI; 例如,用于在java.util.Date和java.lang.Long之间进行转换。在客户端环境(如Web应用程序)中工作时需要使用Formatter SPI,并且需要解析和打印本地化的字段值。ConversionService为两个SPI提供统一的类型转换API。 1.格式化器SPI 格式化器SPI实现字段格式化逻辑很简单并且强类型化: package org.springframework.format; public interface Formatter<T> extends Printer<T>, Parser<T> { } Formatter从打印机和解析器构建块接口扩展而来: public interface Printer<T> { String print(T fieldValue, Locale locale); } import java.text.ParseException; public interface Parser<T> { T parse(String clientValue, Locale locale) throws ParseException; } 要创建自己的格式化程序,只需实现上面的格式化接口即可。例如,将T指定为要格式化的对象的类型 java.util.Date。实施print()操作以打印T的实例以在客户端区域设置中显示。实现parse()操作以从客户机语言环境返回的格式化表示中解析T的实例。如果解析尝试失败,则格式化程序应该抛出ParseException或IllegalArgumentException。注意确保您的Formatter实现是线程安全的。 format为方便起见,在子包中提供了几个格式化器实现。该number包提供了一个NumberFormatter,CurrencyFormatter并 使用a PercentFormatter来格式化java.lang.Number对象java.text.NumberFormat。该datetime包提供了一个用a DateFormatter来格式化java.util.Date对象java.text.DateFormat。该datetime.joda软件包提供基于Joda-Time库的综合日期时间格式化支持。 考虑DateFormatter作为一个示例Formatter实现: package org.springframework.format.datetime; public final class DateFormatter implements Formatter<Date> { private String pattern; public DateFormatter(String pattern) { this.pattern = pattern; } public String print(Date date, Locale locale) { if (date == null) { return ""; } return getDateFormat(locale).format(date); } public Date parse(String formatted, Locale locale) throws ParseException { if (formatted.length() == 0) { return null; } return getDateFormat(locale).parse(formatted); } protected DateFormat getDateFormat(Locale locale) { DateFormat dateFormat = new SimpleDateFormat(this.pattern, locale); dateFormat.setLenient(false); return dateFormat; } } 2.注释驱动的格式 您将会看到,字段格式可以通过字段类型或注释进行配置。要将注释绑定到格式化程序,请实现AnnotationFormatterFactory: package org.springframework.format; public interface AnnotationFormatterFactory<A extends Annotation> { Set<Class<?>> getFieldTypes(); Printer<?> getPrinter(A annotation, Class<?> fieldType); Parser<?> getParser(A annotation, Class<?> fieldType); } 例如,参数化A是您希望将格式逻辑与之关联的字段批注类型org.springframework.format.annotation.DateTimeFormat。已经 getFieldTypes()返回类型字段中的注释,可以使用上。已经 getPrinter()返回打印机打印的注释字段的值。已经 getParser()返回解析器解析一个clientValue一个注释字段。 下面的示例AnnotationFormatterFactory实现将@NumberFormat注释绑定到格式化程序。此注释允许指定数字样式或模式: public final class NumberFormatAnnotationFormatterFactory implements AnnotationFormatterFactory<NumberFormat> { public Set<Class<?>> getFieldTypes() { return new HashSet<Class<?>>(asList(new Class<?>[] { Short.class, Integer.class, Long.class, Float.class, Double.class, BigDecimal.class, BigInteger.class })); } public Printer<Number> getPrinter(NumberFormat annotation, Class<?> fieldType) { return configureFormatterFrom(annotation, fieldType); } public Parser<Number> getParser(NumberFormat annotation, Class<?> fieldType) { return configureFormatterFrom(annotation, fieldType); } private Formatter<Number> configureFormatterFrom(NumberFormat annotation, Class<?> fieldType) { if (!annotation.pattern().isEmpty()) { return new NumberFormatter(annotation.pattern()); } else { Style style = annotation.style(); if (style == Style.PERCENT) { return new PercentFormatter(); } else if (style == Style.CURRENCY) { return new CurrencyFormatter(); } else { return new NumberFormatter(); } } } } 要触发格式化,只需使用@NumberFormat注释字段即可: public class MyModel { @NumberFormat(style=Style.CURRENCY) private BigDecimal decimal; } 格式注释API org.springframework.format.annotation 包中存在可移植的格式注释API 。使用@NumberFormat格式化java.lang.Number字段。使用@DateTimeFormat格式化java.util.Date,java.util.Calendar,java.util.Long或Joda-Time字段。 以下示例使用@DateTimeFormat将java.util.Date格式化为ISO日期(yyyy-MM-dd): public class MyModel { @DateTimeFormat(iso=ISO.DATE) private Date date; } 3.FormatterRegistry SPI FormatterRegistry是用于注册格式化程序和转换程序的SPI。 FormattingConversionService是适用于大多数环境的FormatterRegistry的实现。这个实现可以通过编程或声明方式配置为Spring bean FormattingConversionServiceFactoryBean。因为这个实现也实现了ConversionService,所以它可以直接配置用于Spring的DataBinder和Spring表达式语言(SpEL)。 查看下面的FormatterRegistry SPI: package org.springframework.format; public interface FormatterRegistry extends ConverterRegistry { void addFormatterForFieldType(Class<?> fieldType, Printer<?> printer, Parser<?> parser); void addFormatterForFieldType(Class<?> fieldType, Formatter<?> formatter); void addFormatterForFieldType(Formatter<?> formatter); void addFormatterForAnnotation(AnnotationFormatterFactory<?, ?> factory); } 如上所示,格式化程序可以通过fieldType或注释进行注册。 FormatterRegistry SPI允许您集中配置格式化规则,而不是在控制器中复制这种配置。例如,您可能希望强制所有日期字段以特定方式格式化,或者使用特定批注的字段以特定方式格式化。通过共享的FormatterRegistry,您可以定义这些规则一次,并在需要格式化时应用这些规则。 4.FormatterRegistrar SPI FormatterRegistrar是一个用于通过FormatterRegistry注册格式化器和转换器的SPI: package org.springframework.format; public interface FormatterRegistrar { void registerFormatters(FormatterRegistry registry); } FormatterRegistrar在为给定格式类别(例如日期格式)注册多个相关转换器和格式化程序时非常有用。在声明性注册不足的情况下,它也很有用。例如,格式化程序需要在与其自己的不同的特定字段类型下或在注册打印机/解析器对时进行索引。下一节提供了有关转换器和格式化程序注册的更多信息。 三、配置全球日期和时间格式 默认情况下,不使用注释的日期和时间字段@DateTimeFormat使用DateFormat.SHORT样式从字符串转换。如果你愿意,你可以通过定义你自己的全局格式来改变它。 您需要确保Spring不会注册默认格式化程序,而应该手动注册所有格式化程序。根据您是否使用Joda-Time库,使用 org.springframework.format.datetime.joda.JodaTimeFormatterRegistraror或 org.springframework.format.datetime.DateFormatterRegistrarclass。 例如,以下Java配置将注册全局“yyyyMMdd”格式。这个例子不依赖于Joda-Time库: @Configuration public class AppConfig { @Bean public FormattingConversionService conversionService() { // Use the DefaultFormattingConversionService but do not register defaults DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false); // Ensure @NumberFormat is still supported conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory()); // Register date conversion with a specific global format DateFormatterRegistrar registrar = new DateFormatterRegistrar(); registrar.setFormatter(new DateFormatter("yyyyMMdd")); registrar.registerFormatters(conversionService); return conversionService; } } 如果你更喜欢基于XML的配置,你可以使用a FormattingConversionServiceFactoryBean。这是同一个例子,这次使用乔达时间: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="registerDefaultFormatters" value="false" /> <property name="formatters"> <set> <bean class="org.springframework.format.number.NumberFormatAnnotationFormatterFactory" /> </set> </property> <property name="formatterRegistrars"> <set> <bean class="org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar"> <property name="dateFormatter"> <bean class="org.springframework.format.datetime.joda.DateTimeFormatterFactoryBean"> <property name="pattern" value="yyyyMMdd"/> </bean> </property> </bean> </set> </property> </bean> </beans> 乔达时提供单独的不同类型来表示date,time和date-time 的值。的dateFormatter,timeFormatter和dateTimeFormatter的性质 JodaTimeFormatterRegistrar,应使用来配置不同的格式为每种类型。在DateTimeFormatterFactoryBean提供了一个方便的方法来创建格式化。 如果您使用Spring MVC,请记住明确配置使用的转换服务。对于基于Java的@Configuration这意味着扩展 WebMvcConfigurationSupport类和重写mvcConversionService()方法。对于XML,您应该使用元素的’conversion-service’属性 mvc:annotation-driven。有关详细信息,请参阅转换和格式。