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属性。 有关详细信息,请参阅转换和格式。