Spring3
引入了core.convert
包,提供了通用類型轉換系統
,定義了實現類型轉換和運行時執行類型的SPI
。java
在Spring3.0
以前,提供的PropertyEditor
來將外部化bean屬性值字符串轉換成必需的實現類型。spring
Converter SPI
/** * A converter converts a source object of type {@code S} to a target of type {@code T}. * * <p>Implementations of this interface are thread-safe and can be shared. * * <p>Implementations may additionally implement {@link ConditionalConverter}. * * @author Keith Donald * @since 3.0 * @param <S> the source type * @param <T> the target type */ @FunctionalInterface public interface Converter<S, T> { /** * Convert the source object of type {@code S} to target type {@code T}. * @param source the source object to convert, which must be an instance of {@code S} (never {@code null}) * @return the converted object, which must be an instance of {@code T} (potentially {@code null}) * @throws IllegalArgumentException if the source cannot be converted to the desired target type */ @Nullable T convert(S source); }
實現自定義的類型轉換能夠實現Converter
接口。可是若是S是集合或者數組
轉換爲T的集合或者數組
,數組
建議參考諸如ArrayToCollectionConverter
實現。前提是已經註冊了委託數組或集合轉換器
。例如,安全
DefaultConversionService
實現。多線程
Converter.convert(S source)中source確保不能爲null,不然轉換器可能拋出異常若是轉換失敗。
具體mvc
說,應該會拋出IllegalArgumentException
報告不合理的轉換源。確保Converter
實現是線程安全
。框架
在core.convert.support
包下,註冊了常見了類型轉換器。例如:ide
/** * Converts from a String any JDK-standard Number implementation. * * <p>Support Number classes including Byte, Short, Integer, Float, Double, Long, BigInteger, BigDecimal. This class * delegates to {@link NumberUtils#parseNumber(String, Class)} to perform the conversion. * * @author Keith Donald * @since 3.0 * @see java.lang.Byte * @see java.lang.Short * @see java.lang.Integer * @see java.lang.Long * @see java.math.BigInteger * @see java.lang.Float * @see java.lang.Double * @see java.math.BigDecimal * @see NumberUtils */ final class StringToNumberConverterFactory implements ConverterFactory<String, Number> { @Override public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) { return new StringToNumber<>(targetType); } private static final class StringToNumber<T extends Number> implements Converter<String, T> { private final Class<T> targetType; public StringToNumber(Class<T> targetType) { this.targetType = targetType; } @Override public T convert(String source) { if (source.isEmpty()) { return null; } return NumberUtils.parseNumber(source, this.targetType); } } }
ConverterFactory
當你須要集中整理類層次結構的類型轉換器,可使用ConverterFactory
。例如StringToNumberConverterFactory,this
該接口定義以下,當你須要範圍轉換器,能夠轉換這些對象從S類型轉換成R的子類型。使用該接口
。spa
/** * A factory for "ranged" converters that can convert objects from S to subtypes of R. * * <p>Implementations may additionally implement {@link ConditionalConverter}. * * @author Keith Donald * @since 3.0 * @see ConditionalConverter * @param <S> the source type converters created by this factory can convert from * @param <R> the target range (or base) type converters created by this factory can convert to; * for example {@link Number} for a set of number subtypes. */ public interface ConverterFactory<S, R> { /** * Get the converter to convert from S to target type T, where T is also an instance of R. * @param <T> the target type * @param targetType the target type to convert to * @return a converter from S to T */ <T extends R> Converter<S, T> getConverter(Class<T> targetType); } /** * Converts from a String any JDK-standard Number implementation. * * <p>Support Number classes including Byte, Short, Integer, Float, Double, Long, BigInteger, BigDecimal. This class * delegates to {@link NumberUtils#parseNumber(String, Class)} to perform the conversion. * * @author Keith Donald * @since 3.0 * @see java.lang.Byte * @see java.lang.Short * @see java.lang.Integer * @see java.lang.Long * @see java.math.BigInteger * @see java.lang.Float * @see java.lang.Double * @see java.math.BigDecimal * @see NumberUtils */ final class StringToNumberConverterFactory implements ConverterFactory<String, Number> { @Override public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) { return new StringToNumber<>(targetType); } private static final class StringToNumber<T extends Number> implements Converter<String, T> { private final Class<T> targetType; public StringToNumber(Class<T> targetType) { this.targetType = targetType; } @Override public T convert(String source) { if (source.isEmpty()) { return null; } return NumberUtils.parseNumber(source, this.targetType); } } }
GenericConverter
GenericConverter
提供多種源和目標類型之間轉換
,比Converter更靈活可是對類型要求不高
。它提供了實現
轉換邏輯的源和目標上下文
。 這樣的上下文容許類型轉換由字段註釋或在字段簽名上聲明的通用信息驅動。接口
以下:
package org.springframework.core.convert.converter; public interface GenericConverter { public Set<ConvertiblePair> getConvertibleTypes(); Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); }
ConvertiblePair
持有轉換源和目標類型對
。convert(Object, TypeDescriptor, TypeDescriptor)
。
源TypeDescriptor
提供對保存正在轉換的值的源字段的訪問。 目標TypeDescriptor
提供對要設置轉換值的目標字段的訪問。TypeDescriptor
類是關於要轉換類型的上下文
。
一個好的實例是GenericConverter
在Java數組和集合之間轉換。例如ArrayToCollectionConverter
。
注意
由於GenericConverter是一個更復雜的SPI接口,因此只有在須要時才應該使用它.喜歡Converter或ConverterFactory以知足基本的類型轉換需求。
ConditionalGenericConverter
該接口是一個帶有判斷條件的類型轉換器。該接口是GenericConverter
和ConditionalConverter
的組合。
/** * A {@link GenericConverter} that may conditionally execute based on attributes * of the {@code source} and {@code target} {@link TypeDescriptor}. * * <p>See {@link ConditionalConverter} for details. * * @author Keith Donald * @author Phillip Webb * @since 3.0 * @see GenericConverter * @see ConditionalConverter */ public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter { }/** * A {@link GenericConverter} that may conditionally execute based on attributes * of the {@code source} and {@code target} {@link TypeDescriptor}. * * <p>See {@link ConditionalConverter} for details. * * @author Keith Donald * @author Phillip Webb * @since 3.0 * @see GenericConverter * @see ConditionalConverter */ public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter { }
ConditionalGenericConverter
的一個好示例是StringToCollectionConverter
/** * Converts a comma-delimited String to a Collection. * If the target collection element type is declared, only matches if * {@code String.class} can be converted to it. * * @author Keith Donald * @author Juergen Hoeller * @since 3.0 */ final class StringToCollectionConverter implements ConditionalGenericConverter { private final ConversionService conversionService; public StringToCollectionConverter(ConversionService conversionService) { this.conversionService = conversionService; } @Override public Set<ConvertiblePair> getConvertibleTypes() { return Collections.singleton(new ConvertiblePair(String.class, Collection.class)); } @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { return (targetType.getElementTypeDescriptor() == null || this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor())); } @Override @Nullable public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { if (source == null) { return null; } String string = (String) source; String[] fields = StringUtils.commaDelimitedListToStringArray(string); TypeDescriptor elementDesc = targetType.getElementTypeDescriptor(); Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), (elementDesc != null ? elementDesc.getType() : null), fields.length); if (elementDesc == null) { for (String field : fields) { target.add(field.trim()); } } else { for (String field : fields) { Object targetElement = this.conversionService.convert(field.trim(), sourceType, elementDesc); target.add(targetElement); } } return target; } }
ConversionService API
ConversionService定義了一個統一的API,用於在運行時執行類型轉換邏輯
. 轉換器一般在如下Facade接口後面執行。
package org.springframework.core.convert; public interface ConversionService { boolean canConvert(Class<?> sourceType, Class<?> targetType); <T> T convert(Object source, Class<T> targetType); boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType); Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); }
大多數ConversionService實現,一樣也實現了ConverterRegistry
,該接口提供了SPI
來註冊Converters
.
在內部,ConversionService
的實現,容器委託它來註冊轉換器來執行轉換邏輯。
core.convert.support
提供一個強大的ConversionService
實現,該實現是GenericConversionSer
,它適用於大多數轉換器環境實現。ConversionServiceFactory
來建立普通的ConversionService
配置。
ConversionService
ConversionService
被設計成無狀態對象
,在容器
啓動時被實例化,在多線程間進行共享(線程安全)。
在Spring應用中,能夠自定義類型轉換器
。當須要框架進行類型轉換時,Spring會選擇合適的類型轉換器
使用。你也能夠注入ConversionService
到beans或者直接調用。
注意
若是沒有
ConversionService
註冊到Spring容器,基於的PropertyEditor
實現的類型轉換會被使用。
使用以下的方式,註冊默認ConversionService進
Spring容器中:
public class ConvertersConfiguration { @Bean(name = "conversionService") public ConversionServiceFactoryBean conversionServiceFactory() { ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean(); return conversionServiceFactoryBean; } }
默認的ConversionService
能夠在字符串,數字,枚舉,集合,映射和其餘常見類型之間進行轉換
。要使用您本身的自定義轉換器補充或覆蓋默認轉換器,請設置converter屬性.屬性值能夠實現任何Converter,ConverterFactory或GenericConverter接口。默認ConversionService
實現是DefaultConversionService
。
public class ConvertersConfiguration { @Bean(name = "conversionService") public ConversionServiceFactoryBean conversionServiceFactory() { ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean(); //實現自定義的類型轉換器 conversionServiceFactoryBean.setConverters(Collections.singleton(new StringToDateConverter())); return conversionServiceFactoryBean; } }
也可使用ConversionService
在Spring MVC應用中,參考WebMvcConfigurationSupport
類,該類方法
addFormatters(FormatterRegistry registry)
能夠註冊自定義的converters
。
在某些狀況,但願在類型轉換期間須要格式化,參考FormatterRegistry
。
在程序中使用ConversionService
@Service public class MyService { @Autowired public MyService(ConversionService conversionService) { this.conversionService = conversionService; } public void doIt() { this.conversionService.convert(...) } }
core.convert
是一個通用的類型轉換系統
.它提供了統一的ConversionService API以及強類型轉換器SPI,用於實現從一種類型到另外一種類型的轉換邏輯.Spring容器使用這個系統來綁定bean屬性值
。額外的,還要SpEL
和
DataBinder
。Spring3
引入了Formatter SPI
來實現格式化屬性值。ConversionService
爲兩個SPI提供統一的類型轉換API。
Formatter SPI
/** * Formats objects of type T. * A Formatter is both a Printer <i>and</i> a Parser for an object type. * * @author Keith Donald * @since 3.0 * @param <T> the type of object this Formatter formats */ public interface Formatter<T> extends Printer<T>, Parser<T> { } /** * Parses text strings to produce instances of T. * * @author Keith Donald * @since 3.0 * @param <T> the type of object this Parser produces */ @FunctionalInterface public interface Parser<T> { /** * Parse a text String to produce a T. * @param text the text string * @param locale the current user locale * @return an instance of T * @throws ParseException when a parse exception occurs in a java.text parsing library * @throws IllegalArgumentException when a parse exception occurs */ T parse(String text, Locale locale) throws ParseException; } /** * Prints objects of type T for display. * * @author Keith Donald * @since 3.0 * @param <T> the type of object this Printer prints */ @FunctionalInterface public interface Printer<T> { /** * Print the object of type T for display. * @param object the instance to print * @param locale the current user locale * @return the printed text string */ String print(T object, Locale locale); }
Annotation-Driven Formatting
域格式化能夠經過域類型或者註解配置.
爲了綁定註解在一個Formatter
,實現AnnotationFormatterFactory
.
package org.springframework.format; /** * A factory that creates formatters to format values of fields annotated with a particular * {@link Annotation}. * * <p>For example, a {@code DateTimeFormatAnnotationFormatterFactory} might create a formatter * that formats {@code Date} values set on fields annotated with {@code @DateTimeFormat}. * * @author Keith Donald * @since 3.0 * @param <A> the annotation type that should trigger formatting */ public interface AnnotationFormatterFactory<A extends Annotation> { Set<Class<?>> getFieldTypes(); Printer<?> getPrinter(A annotation, Class<?> fieldType); Parser<?> getParser(A annotation, Class<?> fieldType); } 例如實現NumberFormatAnnotationFormatterFactory,綁定@NumberFormat註解到Formatter。 public class NumberFormatAnnotationFormatterFactory extends EmbeddedValueResolutionSupport implements AnnotationFormatterFactory<NumberFormat> { @Override public Set<Class<?>> getFieldTypes() { return NumberUtils.STANDARD_NUMBER_TYPES; } @Override public Printer<Number> getPrinter(NumberFormat annotation, Class<?> fieldType) { return configureFormatterFrom(annotation); } @Override public Parser<Number> getParser(NumberFormat annotation, Class<?> fieldType) { return configureFormatterFrom(annotation); } private Formatter<Number> configureFormatterFrom(NumberFormat annotation) { String pattern = resolveEmbeddedValue(annotation.pattern()); if (StringUtils.hasLength(pattern)) { return new NumberStyleFormatter(pattern); } else { Style style = annotation.style(); if (style == Style.CURRENCY) { return new CurrencyStyleFormatter(); } else if (style == Style.PERCENT) { return new PercentStyleFormatter(); } else { return new NumberStyleFormatter(); } } } }
DateTimeFormat
和NumberFormat
。
FormatterRegistry SPI
FormatterRegistry
是用來註冊formatters 和 converters
的SPI
。FormattingConversionService
是FormatterRegistry
一個實現,能夠支持大多數環境。能夠經過FormattingConversionServiceFactoryBean
來配置。也能夠經過Spring's DataBinder
和SpEL
。
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); }
FormatterRegistrar SPI
FormatterRegistrar
是經過FormatterRegistry
註冊formatters和converters的SPI
。
package org.springframework.format; public interface FormatterRegistrar { void registerFormatters(FormatterRegistry registry); }
Configuration @Slf4j public class WebConfiguration extends WebMvcConfigurationSupport { @Override protected void addFormatters(FormatterRegistry registry) { registry.addConverter(new StringToDateConverter()); } }
JodaTimeFormatterRegistrar
和DateFormatterRegistrar
,使用Joda須要引入joda庫
配置以下:
@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; } }
注意
Joda-Time提供不一樣類型表示日期
date,time,datetime
,須要經過JodaTimeFormatterRegistrar
進行註冊。或者使用
DateTimeFormatterFactoryBean
來進行建立formatters。
若是您使用Spring MVC,請記住明確配置使用的轉換服務.對於基於Java的@Configuration,這意味着擴展WebMvcConfigurationSupport類並覆蓋mvcConversionService()方法.對於XML,您應該使用mvc:annotation-driven元素的conversion-service屬性。 有關詳細信息,請參閱轉換和格式。