Spring Type Conversion(Spring类型转换)

1:概述:

Spring3引入了core.convert包,提供了通用类型转换系统,定义了实现类型转换和运行时执行类型的SPI

Spring3.0之前,提供的PropertyEditor来将外部化bean属性值字符串转换成必需的实现类型。

2: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);
}
}

}

3: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);
}
}

}

4: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以满足基本的类型转换需求。

5:ConditionalGenericConverter

该接口是一个带有判断条件的类型转换器。该接口是GenericConverterConditionalConverter的组合。

/**
* 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;
}

}

6: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

配置。

7:配置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(...)
}
}

8:Spring域属性格式化

core.convert是一个通用的类型转换系统.它提供了统一的ConversionService API以及强类型转换器SPI,用于实现从一种类型到另一种类型的转换逻辑.Spring容器使用这个系统来绑定bean属性值。额外的,还要SpEL

DataBinderSpring3引入了Formatter SPI来实现格式化属性值。ConversionService为两个SPI提供统一的类型转换API。

(1):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);

}
(2):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();
}
}
}
}
(3):格式化注解API

DateTimeFormatNumberFormat

(4):FormatterRegistry SPI

FormatterRegistry是用来注册formatters 和 convertersSPIFormattingConversionService

FormatterRegistry 一个实现,可以支持大多数环境。可以通过FormattingConversionServiceFactoryBean

来配置。也可以通过Spring's DataBinderSpEL

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);
}
(5):FormatterRegistrar SPI

FormatterRegistrar是通过FormatterRegistry注册formatters和converters的SPI

package org.springframework.format;

public interface FormatterRegistrar {

void registerFormatters(FormatterRegistry registry);
}

9:在Spring MVC配置Formatting

Configuration
@Slf4j
public class WebConfiguration extends WebMvcConfigurationSupport {


@Override
protected void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToDateConverter());
}
}

10:配置全局的Date和时间Format

JodaTimeFormatterRegistrarDateFormatterRegistrar,使用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类型转换)的更多相关文章

  1. Spring Type Conversion(Spring类型转换源码探究)

    1:概述 类型转换系统负责Spring框架中对象类型转换和格式化工作. ConversionService默认实现UML图如下所示: GenericConversionService(通用类型转换服务 ...

  2. [C++] Type Conversion(类型转换)

    Type Conversion(类型转换) Two kinds of type conversion explict type conversion(显式类型转换) impict type conve ...

  3. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(一)

    题外话:本篇是对之前那篇的重排版.并拆分成两篇,免得没了看的兴趣. 前言 在Spring Framework官方文档中,这三者是放到一起讲的,但没有解释为什么放到一起.大概是默认了读者都是有相关经验的 ...

  4. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(二)

    接前一篇 Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 本篇主要内容:Spring Type Conver ...

  5. go学习笔记-类型转换(Type Conversion)

    类型转换(Type Conversion) 类型转换用于将一种数据类型的变量转换为另外一种类型的变,基本格式 type_name(expression) type_name 为类型,expressio ...

  6. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion

    本篇太乱,请移步: Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 写了删删了写,反复几次,对自己的描述很不 ...

  7. 0060 Spring MVC的数据类型转换--ConversionService--局部PropertyEditor--全局WebBindingInitializer

    浏览器向服务器提交的数据,多是字符串形式,而有些时候,浏览器需要Date.Integer等类型的数据,这时候就需要数据类型的转换器 使用Spring的ConversionService及转换器接口 下 ...

  8. Spring对象绑定与类型转换

    Spring对象绑定与类型转换 Spring的框架体系中,到处充斥着对象绑定从bean的初始化autowired属性,SpringMvc 中对对象的绑定等.Spring对象绑定和类型转换在Spring ...

  9. delphi 10.1 berlin datasnap提交clientdataset.delta报:invalid variant type conversion(类型转换错误)问题的解决

    delphi 10.1 berlin datasnap提交clientdataset.delta报:invalid variant type conversion(类型转换错误)问题的解决,需要打这个 ...

随机推荐

  1. 【a302】&&【9306】贮油点问题

    Time Limit: 1 second Memory Limit: 2 MB 问题描述 一辆重型卡车欲穿过1000公里的沙漠,卡车耗油为1升/公里,卡车总载油能力为500公升.显然卡车装 一次油是过 ...

  2. Java 联系Oracle 数据库

    一般完成 import oracle.jdbc.driver.*; 会后声明,你会发现一个错误.然后,你需要: 一.该JDBC开车到classpath 两种方法.一是图形化,计算机-属性-高级设置-环 ...

  3. Struts2——(2)配置文件、通配符

    一.Struts配置文件 (1)struts-default.xml(框架自带) 定义了一些框架自带的Result组件,拦截器组件. <package name="struts-def ...

  4. 2013级别C++文章9周(春天的)工程——运算符重载(两)

    课程主页中:http://blog.csdn.net/sxhelijian/article/details/11890759,内有完整教学方案及资源链接 [程序阅读]阅读程序"简单C++学生 ...

  5. go语言刷leetcode - 14 Longest Common Prefix

    func longestCommonPrefix(strs []string) string { { return "" } { ] } ; ; idx++ { ; i < ...

  6. cocos2d-x 打开控制面板

    于cocos2dx反过来,我们所熟悉的控制台输出,可以查看日志,例如C介面printf();性能. int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTA ...

  7. Leetcode 237 Delete Node in a Linked List 链表

    如题 删除是要注意让现在的链表等于下一个链表的值 class Solution { public: void deleteNode(ListNode* node) { ListNode *nextno ...

  8. WPF-WPF BitmapEffect (按钮凹凸效果)

    原文:WPF-WPF BitmapEffect (按钮凹凸效果) BitmapEffect位图效果是简单的像素处理操作.它可以呈现下面几种特殊效果.              BevelBitmapE ...

  9. QSplitter实现自由伸缩滑动窗口部件(要在m_pSplitter中加入frame_4之前,给frame_4设置样式;之后设置无效)

    实现代码如下: #include <QSplitter> QSplitter *m_pSplitter; m_pSplitter = new QSplitter(ui->frame_ ...

  10. uwp 沉浸式状态栏

    //隐藏状态栏if (ApiInformation.IsTypePresent(typeof(StatusBar).ToString())) { StatusBar statusBar = Statu ...