Spring ConversionService 类型转换(二) ConversionService
Spring ConversionService 类型转换(二) ConversionService
Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)
Spring ConversionService 类型转换系列文章:
在上一篇文章中提到了 Spring 3.0 提供了三种类型的转换器(Converter、ConverterFactory、GenericConverter),分别用来处理 1:1、1:N、N:N 的类型转换。那肯定要有一个类来统一管理所有的类型转换器,负责注册、查找、转换等功能,统一对外提供服务,这个类就是 ConversionService。
一、ConversionService 接口说明

ConversionService类型转换。ConverterRegistry转换器注册、删除、查找功能。ConfigurableConversionService集合了上面两个接口的功能。GenericConversionService实现了 ConfigurableConversionService 接口,Spring 使用的 ConversionService 都是基于这个类的扩展。DefaultConversionService扩展 GenericConversionService,注册了一批默认的转换器。
// 类型转换
public interface ConversionService {
boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
<T> T convert(@Nullable Object source, Class<T> targetType);
Object convert(@Nullable Object source,
@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
}
// 三种类型的转换器的添加和删除
public interface ConverterRegistry {
void addConverter(Converter<?, ?> converter);
<S, T> void addConverter(Class<S> sourceType, Class<T> targetType,
Converter<? super S, ? extends T> converter);
void addConverter(GenericConverter converter);
void addConverterFactory(ConverterFactory<?, ?> factory);
void removeConvertible(Class<?> sourceType, Class<?> targetType);
}
public interface ConfigurableConversionService extends ConversionService, ConverterRegistry {
}
二、DefaultConversionService
ConversionService 有一个默认的实现 DefaultConversionService,这个类在初始化时会添加 Spring 默认的转换器,大部分时候使用这个实现就可以完成所需要的功能。
public DefaultConversionService() {
addDefaultConverters(this);
}
// DefaultConversionService 唯一的功能就是注册默认的转换器
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
// 1. 基础的标准转换器
addScalarConverters(converterRegistry);
// 2. 集合类型
addCollectionConverters(converterRegistry);
// 3. 其他扩展
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToTimeZoneConverter());
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
三、GenericConversionService
GenericConversionService 实现了 ConversionService, ConverterRegistry 两个接口的功能,上面提到的 DefaultConversionService 就是基于 GenericConversionService 的扩展,只是注册了一些默认的转换器。
3.1 Converters
Converters 是 GenericConversionService 中的内部类,负责所有转换器的添加、删除、查找。转换器有两种:一种指定转换的类型;一种没有指定,属于通用的转换器:
// 全局通用的转换器
private final Set<GenericConverter> globalConverters = new LinkedHashSet<>();
// 指定转换类型的转换器,ConvertiblePair 是 GenericConverter 接口的内部类,包含 sourceType 和 targetType
private final Map<ConvertiblePair, ConvertersForPair> converters = new LinkedHashMap<>(36);
对于三种转换器 Converter、ConverterFactory、GenericConverter 在添加到 Converters 中时都会进行适配成 GenericConverter,方便统一管理。这两个适配器都是 GenericConversionService 的内部类,ConverterAdapter 和 ConverterFactoryAdapter 都实现了 ConditionalGenericConverter 接口。
如果 ConvertiblePair(sourceType 和 targetType) 对应多个转换器则统一存储在 ConvertersForPair 中,ConvertersForPair 也是 GenericConversionService 的内部类,它内部维护了一个 LinkedList converters 数组。
(1) add
public void add(GenericConverter converter) {
Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
// 1. globalConverters,没有指定 convertibleTypes 则根据 ConditionalConverter#matches 匹配
if (convertibleTypes == null) {
Assert.state(converter instanceof ConditionalConverter,
"Only conditional converters may return null convertible types");
this.globalConverters.add(converter);
// 2. converters,指定 convertibleTypes
} else {
for (ConvertiblePair convertiblePair : convertibleTypes) {
ConvertersForPair convertersForPair = this.converters.get(convertiblePair);
if (convertersForPair == null) {
convertersForPair = new ConvertersForPair();
this.converters.put(convertiblePair, convertersForPair);
}
convertersForPair.add(converter);
}
}
}
(2) remove
public void remove(Class<?> sourceType, Class<?> targetType) {
this.converters.remove(new ConvertiblePair(sourceType, targetType));
}
(3) find
public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
// 获取 sourceType 类的所有父类和接口
List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());
// 获取 targetType 类的所有父类和接口
List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());
for (Class<?> sourceCandidate : sourceCandidates) {
for (Class<?> targetCandidate : targetCandidates) {
ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
// 查找指定 sourceType, targetType, convertiblePair 的转换器
GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
if (converter != null) {
return converter;
}
}
}
return null;
}
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
TypeDescriptor targetType, ConvertiblePair convertiblePair) {
// 1. 首先在 converters 中查找
ConvertersForPair convertersForPair = this.converters.get(convertiblePair);
if (convertersForPair != null) {
GenericConverter converter = convertersForPair.getConverter(sourceType, targetType);
if (converter != null) {
return converter;
}
}
// 2. 再在 globalConverters 中查找
for (GenericConverter globalConverter : this.globalConverters) {
if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {
return globalConverter;
}
}
return null;
}
ConvertersForPair 根据 sourceType 和 targetType 查找可用的 GenericConverter
private static class ConvertersForPair {
private final LinkedList<GenericConverter> converters = new LinkedList<>();
public void add(GenericConverter converter) {
this.converters.addFirst(converter);
}
// 取第一个匹配到的 GenericConverter
public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
for (GenericConverter converter : this.converters) {
if (!(converter instanceof ConditionalGenericConverter) ||
((ConditionalGenericConverter) converter).matches(sourceType, targetType)) {
return converter;
}
}
return null;
}
}
3.2 GenericConversionService
GenericConversionService 内部有两个属性
// Converters 负责所有转换器的添加、删除、查找,上面已经说了
private final Converters converters = new Converters();
// 缓存已经匹配过后 GenericConverter,避免下次还要查找
private final Map<ConverterCacheKey, GenericConverter> converterCache = new ConcurrentReferenceHashMap<>(64);
(1) 添加
// Converter
public void addConverter(Converter<?, ?> converter) {
ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);
addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));
}
// ConverterFactory
public void addConverterFactory(ConverterFactory<?, ?> factory) {
ResolvableType[] typeInfo = getRequiredTypeInfo(factory.getClass(), ConverterFactory.class);
addConverter(new ConverterFactoryAdapter(factory,
new ConvertiblePair(typeInfo[0].toClass(), typeInfo[1].toClass())));
}
// GenericConverter
public void addConverter(GenericConverter converter) {
this.converters.add(converter);
// 清空 converterCache 缓存中的数据
invalidateCache();
}
添加的操作非常简单,在添加之前先要解析 Converter 或 ConverterFactory 的源类型和目标类型,由 getRequiredTypeInfo 完成。
private ResolvableType[] getRequiredTypeInfo(Class<?> converterClass, Class<?> genericIfc) {
ResolvableType resolvableType = ResolvableType.forClass(converterClass).as(genericIfc);
ResolvableType[] generics = resolvableType.getGenerics();
if (generics.length < 2) {
return null;
}
Class<?> sourceType = generics[0].resolve();
Class<?> targetType = generics[1].resolve();
if (sourceType == null || targetType == null) {
return null;
}
return generics;
}
(2) 查找
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
GenericConverter converter = this.converterCache.get(key);
if (converter != null) {
return (converter != NO_MATCH ? converter : null);
}
// 1. 委托给 converters 完成
converter = this.converters.find(sourceType, targetType);
// 2. 由子类重写,默认判断 sourceType 和 targetType 的类型
// 如不需要转换(targetType 是 sourceType 的子类),直接返回 source
if (converter == null) {
converter = getDefaultConverter(sourceType, targetType);
}
if (converter != null) {
this.converterCache.put(key, converter);
return converter;
}
this.converterCache.put(key, NO_MATCH);
return null;
}
查找其实也很简单,核心步骤 converters.find() 都委托给 converters 完成了。
(3) canConvert
@Override
public boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
return canConvert((sourceType != null ? TypeDescriptor.valueOf(sourceType) : null),
TypeDescriptor.valueOf(targetType));
}
@Override
public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
if (sourceType == null) {
return true;
}
GenericConverter converter = getConverter(sourceType, targetType);
return (converter != null);
}
(4) convert
@Override
public <T> T convert(@Nullable Object source, Class<T> targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType));
}
@Override
public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
// 1. sourceType==null && source==null,返回 null(Optional)
if (sourceType == null) {
Assert.isTrue(source == null, "Source must be [null] if source type == [null]");
return handleResult(null, targetType, convertNullSource(null, targetType));
}
// 2.1. getConverter 获取转换器
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
// 2.2. 执行 converter#convert 方法
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
return handleResult(sourceType, targetType, result);
}
return handleConverterNotFound(source, sourceType, targetType);
}
至于 handleResult 和 handleConverterNotFound 就非常简单了
private Object handleResult(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType,
@Nullable Object result) {
if (result == null) {
assertNotPrimitiveTargetType(sourceType, targetType);
}
return result;
}
参考:
- 《IOC源码-conversionService》:https://www.cnblogs.com/jyyzzjl/p/5478620.html
每天用心记录一点点。内容也许不重要,但习惯很重要!
Spring ConversionService 类型转换(二) ConversionService的更多相关文章
- Spring ConversionService 类型转换(一)Converter
Spring ConversionService 类型转换(一)Converter Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.h ...
- 6. 抹平差异,统一类型转换服务ConversionService
目录 ✍前言 版本约定 ✍正文 ConverterRegistry ConversionService ConfigurableConversionService GenericConversionS ...
- Spring 学习记录3 ConversionService
ConversionService与Environment的关系 通过之前的学习(Spring 学习记录2 Environment),我已经Environment主要是负责解析properties和p ...
- spring 之 类型转换
在spring中, 提供了至少三种的 类型转换方式: ConversionServiceFactoryBean, FormattingConversionServiceFactoryBean, C ...
- Spring Environment(二)源码分析
Spring Environment(二)源码分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring Envi ...
- spring boot / cloud (二) 规范响应格式以及统一异常处理
spring boot / cloud (二) 规范响应格式以及统一异常处理 前言 为什么规范响应格式? 我认为,采用预先约定好的数据格式,将返回数据(无论是正常的还是异常的)规范起来,有助于提高团队 ...
- Spring Data(二)查询
Spring Data(二)查询 接着上一篇,我们继续讲解Spring Data查询的策略. 查询的生成 查询的构建机制对于Spring Data的基础是非常有用的.构建的机制将截断前缀find-By ...
- spring boot / cloud (二十) 相同服务,发布不同版本,支撑并行的业务需求
spring boot / cloud (二十) 相同服务,发布不同版本,支撑并行的业务需求 有半年多没有更新了,按照常规剧本,应该会说项目很忙,工作很忙,没空更新,吧啦吧啦,相关的话吧, 但是细想想 ...
- Spring IOC(二)容器初始化
本系列目录: Spring IOC(一)概览 Spring IOC(二)容器初始化 Spring IOC(三)依赖注入 Spring IOC(四)总结 目录 一.ApplicationContext接 ...
随机推荐
- vue 解决页面闪烁问题
watch: { //监听表格数据的变化[使用 watch+nextTick 可以完成页面数据监听的 不会出现闪烁] tableData: { //深度监听,可监听到对象.数组的变化 handler( ...
- Python基础之-----------函数
---恢复内容开始--- 函数:在其他的语言中,我们也经常听到函数的概念,那么什么是函数呢?在Java中叫做method: 定义:函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函 ...
- SQLMAP自动注入(二)
--data 添加post头 --data 添加get头 --cookie 添加cookie 设置探测级别大于等于2时会探测cookie是否有注入点 --random-agent 随机生成user-a ...
- 九 configparser模块
配置文件如下: # 注释1 ; 注释2 [section1] k1 = v1 k2:v2 user=egon age=18 is_admin=true salary=31 [section2] k1 ...
- 矩形覆盖(python)
题目描述 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形.请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? # -*- coding:utf-8 -*- class S ...
- 第二章 向量(d2)有序向量:二分查找
- 忘记root密码,怎么办
当前账户拥有sudo权限,可以通过sudo passwd root来重置root密码.
- FortiGate基本信息
1.介绍 FortiGate是全新的下一代防火墙,在整个硬件架构和系统上面都有新的设计,在性能和功能上面都有了很大提升,具有性能高.接口丰富.功能齐全.安全路由交换一体化.性价比高等优势. Forti ...
- python脚本删除文件与目录的命令
1. 删除文件的命令 import os os.remove(file) os.unlink(file) 2.删除目录的命令 import shutil shutil.rmtree(directory ...
- Java06-java基础语法(五)数组
Java06-java基础语法(五)数组 一.循环的嵌套 在一个循环体内部再含有一个或多个循环 强调:内循环全部做完以后再去执行下一次的外循环 int k = 0; for(int i = 0; i& ...