Spring Type Conversion(Spring类型转换)
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
该接口是一个带有判断条件的类型转换器。该接口是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;
}
}
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
和
DataBinder
。Spring3
引入了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
DateTimeFormat
和NumberFormat
。
(4):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);
}
(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
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类型转换)的更多相关文章
- Spring Type Conversion(Spring类型转换源码探究)
1:概述 类型转换系统负责Spring框架中对象类型转换和格式化工作. ConversionService默认实现UML图如下所示: GenericConversionService(通用类型转换服务 ...
- [C++] Type Conversion(类型转换)
Type Conversion(类型转换) Two kinds of type conversion explict type conversion(显式类型转换) impict type conve ...
- Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(一)
题外话:本篇是对之前那篇的重排版.并拆分成两篇,免得没了看的兴趣. 前言 在Spring Framework官方文档中,这三者是放到一起讲的,但没有解释为什么放到一起.大概是默认了读者都是有相关经验的 ...
- Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(二)
接前一篇 Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 本篇主要内容:Spring Type Conver ...
- go学习笔记-类型转换(Type Conversion)
类型转换(Type Conversion) 类型转换用于将一种数据类型的变量转换为另外一种类型的变,基本格式 type_name(expression) type_name 为类型,expressio ...
- Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion
本篇太乱,请移步: Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 写了删删了写,反复几次,对自己的描述很不 ...
- 0060 Spring MVC的数据类型转换--ConversionService--局部PropertyEditor--全局WebBindingInitializer
浏览器向服务器提交的数据,多是字符串形式,而有些时候,浏览器需要Date.Integer等类型的数据,这时候就需要数据类型的转换器 使用Spring的ConversionService及转换器接口 下 ...
- Spring对象绑定与类型转换
Spring对象绑定与类型转换 Spring的框架体系中,到处充斥着对象绑定从bean的初始化autowired属性,SpringMvc 中对对象的绑定等.Spring对象绑定和类型转换在Spring ...
- delphi 10.1 berlin datasnap提交clientdataset.delta报:invalid variant type conversion(类型转换错误)问题的解决
delphi 10.1 berlin datasnap提交clientdataset.delta报:invalid variant type conversion(类型转换错误)问题的解决,需要打这个 ...
随机推荐
- 【a302】&&【9306】贮油点问题
Time Limit: 1 second Memory Limit: 2 MB 问题描述 一辆重型卡车欲穿过1000公里的沙漠,卡车耗油为1升/公里,卡车总载油能力为500公升.显然卡车装 一次油是过 ...
- Java 联系Oracle 数据库
一般完成 import oracle.jdbc.driver.*; 会后声明,你会发现一个错误.然后,你需要: 一.该JDBC开车到classpath 两种方法.一是图形化,计算机-属性-高级设置-环 ...
- Struts2——(2)配置文件、通配符
一.Struts配置文件 (1)struts-default.xml(框架自带) 定义了一些框架自带的Result组件,拦截器组件. <package name="struts-def ...
- 2013级别C++文章9周(春天的)工程——运算符重载(两)
课程主页中:http://blog.csdn.net/sxhelijian/article/details/11890759,内有完整教学方案及资源链接 [程序阅读]阅读程序"简单C++学生 ...
- go语言刷leetcode - 14 Longest Common Prefix
func longestCommonPrefix(strs []string) string { { return "" } { ] } ; ; idx++ { ; i < ...
- cocos2d-x 打开控制面板
于cocos2dx反过来,我们所熟悉的控制台输出,可以查看日志,例如C介面printf();性能. int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTA ...
- Leetcode 237 Delete Node in a Linked List 链表
如题 删除是要注意让现在的链表等于下一个链表的值 class Solution { public: void deleteNode(ListNode* node) { ListNode *nextno ...
- WPF-WPF BitmapEffect (按钮凹凸效果)
原文:WPF-WPF BitmapEffect (按钮凹凸效果) BitmapEffect位图效果是简单的像素处理操作.它可以呈现下面几种特殊效果. BevelBitmapE ...
- QSplitter实现自由伸缩滑动窗口部件(要在m_pSplitter中加入frame_4之前,给frame_4设置样式;之后设置无效)
实现代码如下: #include <QSplitter> QSplitter *m_pSplitter; m_pSplitter = new QSplitter(ui->frame_ ...
- uwp 沉浸式状态栏
//隐藏状态栏if (ApiInformation.IsTypePresent(typeof(StatusBar).ToString())) { StatusBar statusBar = Statu ...