Spring Type Conversion(Spring类型转换源码探究)
1:概述
类型转换系统负责Spring框架中对象类型转换和格式化工作。
ConversionService默认实现UML图如下所示:

GenericConversionService(通用类型转换服务),是整个类型转换系统的完整实现。作为容器,
管理转换器,同时调用这些转换器进行类型转换,是一个空的容器,内部没有任何转换器。是线程安全。
2:GenericConversionService(通用类型转换服务)学习
(1):转换器缓存设计
//自定义Map Key实现
private final Map<ConverterCacheKey, GenericConverter> converterCache = new ConcurrentReferenceHashMap<>(64);
/**
* Key for use with the converter cache.
*/
private static final class ConverterCacheKey implements Comparable<ConverterCacheKey> {
private final TypeDescriptor sourceType;
private final TypeDescriptor targetType;
public ConverterCacheKey(TypeDescriptor sourceType, TypeDescriptor targetType) {
this.sourceType = sourceType;
this.targetType = targetType;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof ConverterCacheKey)) {
return false;
}
ConverterCacheKey otherKey = (ConverterCacheKey) other;
return (this.sourceType.equals(otherKey.sourceType)) &&
this.targetType.equals(otherKey.targetType);
}
@Override
public int hashCode() {
return (this.sourceType.hashCode() * 29 + this.targetType.hashCode());
}
@Override
public String toString() {
return ("ConverterCacheKey [sourceType = " + this.sourceType +
", targetType = " + this.targetType + "]");
}
@Override
public int compareTo(ConverterCacheKey other) {
int result = this.sourceType.getResolvableType().toString().compareTo(
other.sourceType.getResolvableType().toString());
if (result == 0) {
result = this.targetType.getResolvableType().toString().compareTo(
other.targetType.getResolvableType().toString());
}
return result;
}
}
(2):转换器类型适配器设计(适配器模式)
/**
* Adapts a {@link Converter} to a {@link GenericConverter}.
*/
@SuppressWarnings("unchecked")
private final class ConverterAdapter implements ConditionalGenericConverter { private final Converter<Object, Object> converter; private final ConvertiblePair typeInfo; private final ResolvableType targetType; public ConverterAdapter(Converter<?, ?> converter, ResolvableType sourceType, ResolvableType targetType) {
this.converter = (Converter<Object, Object>) converter;
this.typeInfo = new ConvertiblePair(sourceType.resolve(Object.class), targetType.resolve(Object.class));
this.targetType = targetType;
} @Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(this.typeInfo);
} @Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
// Check raw type first...
if (this.typeInfo.getTargetType() != targetType.getObjectType()) {
return false;
}
// Full check for complex generic type match required?
ResolvableType rt = targetType.getResolvableType();
if (!(rt.getType() instanceof Class) && !rt.isAssignableFrom(this.targetType) &&
!this.targetType.hasUnresolvableGenerics()) {
return false;
}
return !(this.converter instanceof ConditionalConverter) ||
((ConditionalConverter) this.converter).matches(sourceType, targetType);
} @Override
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return convertNullSource(sourceType, targetType);
}
return this.converter.convert(source);
} @Override
public String toString() {
return (this.typeInfo + " : " + this.converter);
}
} private final class ConverterFactoryAdapter implements ConditionalGenericConverter { private final ConverterFactory<Object, Object> converterFactory; private final ConvertiblePair typeInfo; public ConverterFactoryAdapter(ConverterFactory<?, ?> converterFactory, ConvertiblePair typeInfo) {
this.converterFactory = (ConverterFactory<Object, Object>) converterFactory;
this.typeInfo = typeInfo;
} @Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(this.typeInfo);
} @Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
boolean matches = true;
if (this.converterFactory instanceof ConditionalConverter) {
matches = ((ConditionalConverter) this.converterFactory).matches(sourceType, targetType);
}
if (matches) {
Converter<?, ?> converter = this.converterFactory.getConverter(targetType.getType());
if (converter instanceof ConditionalConverter) {
matches = ((ConditionalConverter) converter).matches(sourceType, targetType);
}
}
return matches;
} @Override
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return convertNullSource(sourceType, targetType);
}
return this.converterFactory.getConverter(targetType.getObjectType()).convert(source);
} @Override
public String toString() {
return (this.typeInfo + " : " + this.converterFactory);
}
}
(3):转换器存储设计
 private final Converters converters = new Converters();
    /**
     * Manages all converters registered with the service.
     */
    private static class Converters {
        private final Set<GenericConverter> globalConverters = new LinkedHashSet<>();
        private final Map<ConvertiblePair, ConvertersForPair> converters = new LinkedHashMap<>(36);
    }
(4):没有操作和没有匹配类型设计
/**
* General NO-OP converter used when conversion is not required.
*/
private static final GenericConverter NO_OP_CONVERTER = new NoOpConverter("NO_OP");
/**
* Used as a cache entry when no converter is available.
* This converter is never returned.
*/
private static final GenericConverter NO_MATCH = new NoOpConverter("NO_MATCH");
/**
* Internal converter that performs no operation.
*/
private static class NoOpConverter implements GenericConverter {
private final String name;
public NoOpConverter(String name) {
this.name = name;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return null;
}
@Override
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return source;
}
@Override
public String toString() {
return this.name;
}
}
总结
适配器合理设计
缓存合理设计
存储合理设计
不匹配和不操作合理设计
读操作设计成一个接口(参照
ConversionService)注册操作设计成一个接口(参照
ConverterRegistry)写操作设计成一个接口(参照
ConfigurableConversionService)
(3):DefaultConversionService源码学习
默认的类型转换系统,继承了GenericConversionService类。在构造方法调用添加默认的转换器。
public class DefaultConversionService extends GenericConversionService {
    @Nullable
    private static volatile DefaultConversionService sharedInstance;
    /**
     * Create a new {@code DefaultConversionService} with the set of
     * {@linkplain DefaultConversionService#addDefaultConverters(ConverterRegistry) default converters}.
     */
    public DefaultConversionService() {
        addDefaultConverters(this);
    }
    /**
     * Return a shared default {@code ConversionService} instance,
     * lazily building it once needed.
     * <p><b>NOTE:</b> We highly recommend constructing individual
     * {@code ConversionService} instances for customization purposes.
     * This accessor is only meant as a fallback for code paths which
     * need simple type coercion but cannot access a longer-lived
     * {@code ConversionService} instance any other way.
     * @return the shared {@code ConversionService} instance (never {@code null})
     * @since 4.3.5
     */
    public static ConversionService getSharedInstance() {
        DefaultConversionService cs = sharedInstance;
        if (cs == null) {
            synchronized (DefaultConversionService.class) {
                cs = sharedInstance;
                if (cs == null) {
                    cs = new DefaultConversionService();
                    sharedInstance = cs;
                }
            }
        }
        return cs;
    }
}
总结
单例模式之双重检锁模式正确运用
(4):ConversionServiceFactoryBean(类型转换器注册工厂Bean)
/**
 * A factory providing convenient access to a ConversionService configured with
 * converters appropriate for most environments. Set the
 * {@link #setConverters "converters"} property to supplement the default converters.
 *
 * <p>This implementation creates a {@link DefaultConversionService}.
 * Subclasses may override {@link #createConversionService()} in order to return
 * a {@link GenericConversionService} instance of their choosing.
 *
 * <p>Like all {@code FactoryBean} implementations, this class is suitable for
 * use when configuring a Spring application context using Spring {@code <beans>}
 * XML. When configuring the container with
 * {@link org.springframework.context.annotation.Configuration @Configuration}
 * classes, simply instantiate, configure and return the appropriate
 * {@code ConversionService} object from a {@link
 * org.springframework.context.annotation.Bean @Bean} method.
 * @since 3.0
 */
public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
    @Nullable
    private Set<?> converters;
    @Nullable
    private GenericConversionService conversionService;
    /**
     * Configure the set of custom converter objects that should be added:
     * implementing {@link org.springframework.core.convert.converter.Converter},
     * {@link org.springframework.core.convert.converter.ConverterFactory},
     * or {@link org.springframework.core.convert.converter.GenericConverter}.
     */
    public void setConverters(Set<?> converters) {
        this.converters = converters;
    }
    @Override
    public void afterPropertiesSet() {
        this.conversionService = createConversionService();
        ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
    }
    /**
     * Create the ConversionService instance returned by this factory bean.
     * <p>Creates a simple {@link GenericConversionService} instance by default.
     * Subclasses may override to customize the ConversionService instance that
     * gets created.
     */
    protected GenericConversionService createConversionService() {
        return new DefaultConversionService();
    }
    // implementing FactoryBean
    @Override
    @Nullable
    public ConversionService getObject() {
        return this.conversionService;
    }
    @Override
    public Class<? extends ConversionService> getObjectType() {
        return GenericConversionService.class;
    }
    @Override
    public boolean isSingleton() {
        return true;
    }
}
/**
 * A factory for common {@link org.springframework.core.convert.ConversionService}
 * configurations.
 * @since 3.0
 */
public abstract class ConversionServiceFactory {
    /**
     * Register the given Converter objects with the given target ConverterRegistry.
     * @param converters the converter objects: implementing {@link Converter},
     * {@link ConverterFactory}, or {@link GenericConverter}
     * @param registry the target registry
     */
    public static void registerConverters(@Nullable Set<?> converters, ConverterRegistry registry) {
        if (converters != null) {
            for (Object converter : converters) {
                if (converter instanceof GenericConverter) {
                    registry.addConverter((GenericConverter) converter);
                }
                else if (converter instanceof Converter<?, ?>) {
                    registry.addConverter((Converter<?, ?>) converter);
                }
                else if (converter instanceof ConverterFactory<?, ?>) {
                    registry.addConverterFactory((ConverterFactory<?, ?>) converter);
                }
                else {
                    throw new IllegalArgumentException("Each converter object must implement one of the " +
                            "Converter, ConverterFactory, or GenericConverter interfaces");
                }
            }
        }
    }
}
总结
简单工厂模式运用
FactoryBean使用
(5):格式化Formatter体系

总结
往Spring类型转换系统靠.
(6):DefaultConversionService可支持转换的列表
格式:sourceType-targetType
//
简单类型Number-->Number
String-->Number Number-->String
String-->Character Character-->String
Number-->Character Character-->Number
String-->Boolean Boolean-->String
String-->Enum Enum-->String
Integer-->Enum Enum-->Integer
String-->Locale Locale-->String
String-->Charset Charset->String
String-->Currency(货币) Currency-->String
String-->Properties Properties-->String
String-->UUID UUID-->String
//
集合类型和数组类型Object[]-->Collection Collection-->Object[]
Object[]-->Object[]
Collection-->Collection
Map-->Map
Object[]-->String String-->Object[]
Object[]-->Object Object-->Object[]
Collection-->String String-->Collection
Collection-->Object Object-->Collection
Stream-->Collection Collection-->Stream
Stream-->Object[] Object[]-->Stream
//
其他类型ByteBuffer-->Object Object-->ByteBuffer
ByteBuffer-->byte[] byte[]-->ByteBuffer
String-->TimeZone
ZoneId-->TimeZone
ZonedDateTime-->Calendar
Object-->Object
Object-->String
Object-->Optional
Collection-->Optional
Object[]-->Optional
Spring Type Conversion(Spring类型转换源码探究)的更多相关文章
- Spring Type Conversion(Spring类型转换)
		
Spring Type Conversion(Spring类型转换) 1:概述: Spring3引入了core.convert包,提供了通用类型转换系统,定义了实现类型转换和运行时执行类型的SPI. ...
 - Mybatis的初始化和结合Spring Framework后初始化的源码探究
		
带着下面的问题进行学习: (1)Mybatis 框架或 Spring Framework 框架对数据层 Mapper 接口做了代理,那是做了 JDK 动态代理还是 CGLIB 代理? (2)Mappe ...
 - Mybatis一级缓存和结合Spring Framework后失效的源码探究
		
1.在下面的案例中,执行两次查询控制台只会输出一次 SQL 查询: mybatis-config.xml <?xml version="1.0" encoding=" ...
 - Spring Framework自动装配setAutowireMode和Mybatis案例的源码探究
		
由前文可得知, Spring Framework的自动装配有两种方式:xml配置和注解配置: 自动装配的类型有: (1)xml配置中的byType根据类型查找(@Autowired注解是默认根据类型查 ...
 - Spring Environment(二)源码分析
		
Spring Environment(二)源码分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring Envi ...
 - 七、Spring之深入理解AOP源码
		
Spring之深入理解AOP源码  在上一篇博文中,我们对AOP有了初步的了解,那么接下来我们就对AOP的实现原理进行深入的分析.  在之前写的那个AOP示例代码当中有这样一个注解:@Enable ...
 - Spring框架之spring-web http源码完全解析
		
Spring框架之spring-web http源码完全解析 Spring-web是Spring webMVC的基础,由http.remoting.web三部分组成. http:封装了http协议中的 ...
 - Spring框架之spring-web web源码完全解析
		
Spring框架之spring-web web源码完全解析 spring-web是Spring webMVC的基础,由http.remoting.web三部分组成,核心为web模块.http模块封装了 ...
 - 一文读懂Spring动态配置多数据源---源码详细分析
		
Spring动态多数据源源码分析及解读 一.为什么要研究Spring动态多数据源  期初,最开始的原因是:想将答题服务中发送主观题答题数据给批改中间件这块抽象出来, 但这块主要使用的是mq消息的方式 ...
 
随机推荐
- burp suite中国乱码的解决方案
			
于http的response还有经常出现乱码,其实解决的办法很easy 首先点击Options标签, 然后找到display 找到http message display watermark/2/te ...
 - 查看系统中安装了那些dotnet core 的SDK和运行时的命令
			
原文:查看系统中安装了那些dotnet core 的SDK和运行时的命令 1.查看SDK dotnet --list-sdks 2.查看运行时 dotnet --list-runtimes 效果如下图 ...
 - android Notification分析——
您可能会遇到各种问题
			
使用的各种总结上线通知,csdn还有一个非常到位的总结,不这样做,反复总结,学生需要能够搜索自己或参考下面给出的链接. 研究开始时仔细阅读一些,今天,功能开发,一些问题和经验自己最近的遭遇给大家分享. ...
 - wpf控件开发基础(3) -属性系统(2)
			
原文:wpf控件开发基础(3) -属性系统(2) 上篇说明了属性存在的一系列问题. 属性默认值,可以保证属性的有效性. 属性验证有效性,可以对输入的属性进行校验 属性强制回调, 即不管属性有无发生变化 ...
 - win10下Linux子系统开启ssh服务
			
原文:win10下Linux子系统开启ssh服务 为了便于交流共同学习,博主QQ群242629020(stm32-MCU认认真真交流群) 欢迎批评指导!!!电梯:https://jq.qq.com/? ...
 - SQLite介绍及使用
			
SQLite,是一款轻型的数据库,是遵守ACID的关联式数据库管理系统,它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了 ...
 - Parse陨落,开发者服务今后路在何方?
			
Parse为开发者提供移动应用的后台服务,包括数据存储.消息推送及用户管理等等.因此方便开发者可专心在客户端的制作,简化服务器端的设计. 关于 Parse 关停 2016年1月28日,Parse 官方 ...
 - html5 模糊匹配搜索框
			
使用bootstrap3-typeahead.js 文件在这里 引用: <script type="text/javascript" src="@Url.Conte ...
 - VC++中的C运行时库浅析(控制台程序默认使用单线程的静态链接库,而MFC中的CFile类已暗藏了多线程)
			
1.概论 运行时库是程序在运行时所需要的库文件,通常运行时库是以LIB或DLL形式提供的.C运行时库诞生于20世纪70年代,当时的程序世界还很单纯,应用程序都是单线程的,多任务或多线程机制在此时还属于 ...
 - String 源码分析
			
Java 源码阅读 - String String 类型看起来简单,实际上背后的复杂性基本可以涵盖了整个 Java 设计,涉及到设计模式(不可变对象).缓存(String Pool 的理念).JVM( ...