Spring3
引入了core.convert
包,提供了通用类型转换系统
,定义了实现类型转换和运行时执行类型的SPI
。
在Spring3.0
之前,提供的PropertyEditor
来将外部化bean属性值字符串转换成必需的实现类型。
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,否则转换器可能抛出异常如果转换失败。
具体
说,应该会抛出IllegalArgumentException
报告不合理的转换源。确保Converter
实现是线程安全
。
在core.convert.support
包下,注册了常见了类型转换器。例如:
/** * 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,
该接口定义如下,当你需要范围转换器,可以转换这些对象从S类型转换成R的子类型。使用该接口
。
/** * 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属性。 有关详细信息,请参阅转换和格式。
Spring Type Conversion(Spring类型转换)
原文:https://www.cnblogs.com/liuenyuan1996/p/11066202.html