SpringMVC 之类型转换Converter 源代码分析
- 最近研究SpringMVC的类型转换器,在以往我们需要 SpringMVC 为我们自动进行类型转换的时候都是用的PropertyEditor 。通过 PropertyEditor 的 setAsText() 方法我们可以实现字符串向特定类型的转换。但是这里有一个限制是它只支持从 String 类型转为其他类型。在Spring3中 引入了Converter<S, T>接口, 它支持从一个 Object 转为另一个 Object 。除了 Converter接口之外,实现 ConverterFactory 接口和 GenericConverter 接口也可以实现我们自己的类型转换逻辑。
- 我们先来看一下Converter<S, T>接口的定义
-
public interface Converter
{ T convert(S source);} - S为源对象 T为目标对象 实现该接口即可实现我们的类型转换,我们写一个最为常见的时间类型转换器
-
1 public class StringToDateConvert implements Converter
{ 2 3 private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); 4 5 @Override 6 public Date convert(String source) { 7 if(source.length() == 0) { 8 return null; 9 }10 try {11 return format.parse(source);12 } catch (ParseException e) {13 throw new RuntimeException(source + "类型转换失败");14 }15 }16 17 } - 在定义好 Converter 之后,就是使用 Converter 了。为了统一调用 Converter 进行类型转换, Spring 为我们提供了一个 ConversionService 接口。通过实现这个接口我们可以实现自己的 Converter 调用逻辑。我们先来看一下 ConversionService 接口的定义:
-
public interface ConversionService { boolean canConvert(Class sourceType, Class targetType); boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
T convert(Object source, Class targetType); Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);} 我们可以看到 ConversionService 接口里面定义了两个 canConvert 方法和两个convert 方法, canConvert 方法用于判断当前的 ConversionService 是否能够对原类型和目标类型进行转换, convert 方法则是用于进行类型转换的。上面出现的参数类型TypeDescriptor 是对于一种类型的封装,里面包含该种类型的值、实际类型等等信息。
- 一般而言我们在实现 ConversionService 接口的时候也会实现 ConverterRegistry 接口。使用 ConverterRegistry 可以使我们对类型转换器做一个统一的注册。ConverterRegistry 接口的定义如下:
-
public interface ConverterRegistry { void addConverter(Converter converter); void addConverter(GenericConverter converter); void addConverterFactory(ConverterFactory converterFactory); void removeConvertible(Class sourceType, Class targetType); }
有了这俩个接口以后我们就可以发挥我们自己的设计能力实现里面的逻辑了。不过Spring已经为我们提供了一个实现,这个类就是GenericConversionService
- 首先我们看一下这个类的定义
-
public class GenericConversionService implements ConfigurableConversionService {}
- 可以看到 GenericConversionService 继承至ConfigurableConversionService 接口,那ConfigurableConversionService 又是什么呢,我们在看源码
-
public interface ConfigurableConversionService extends ConversionService, ConverterRegistry {}
呵呵 这回清楚了吧,GenericConversionService 就是实现了ConversionService接口与ConverterRegistry接口。完成了我们类型转换器的注册 与 转换逻辑,下面我们将通过源代码来详细分析该实现的逻辑。
- 在分析源代码之前,让我们在看另外俩个转换器,也就是上面提到的 ConverterFactory 接口和 GenericConverter
- 看ConverterFactory接口的定义
-
public interface ConverterFactory
{Converter getConverter(ClasstargetType); } 我们可以看到 ConverterFactory 接口里面就定义了一个产生 Converter 的getConverter 方法,参数是目标类型的 class 。我们可以看到 ConverterFactory 中一共用到了三个泛型, S 、 R 、 T ,其中 S 表示原类型, R 表示目标类型, T 是类型 R 的一个子类。
-
考虑这样一种情况,我们有一个表示用户状态的枚举类型 UserStatus ,如果要定义一个从 String 转为 UserStatus 的 Converter ,根据之前 Converter 接口的说明,我们的StringToUserStatus 大概是这个样子:
-
public class StringToUserStatus implements Converter
{ @Override public UserStatus convert(String source) { if (source == null) { return null; } return UserStatus.valueOf(source); } } 如果这个时候有另外一个枚举类型 UserType ,那么我们就需要定义另外一个从String 转为 UserType 的 Converter —— StringToUserType ,那么我们的StringToUserType 大概是这个样子:
-
public class StringToUserType implements Converter
{ @Override public UserType convert(String source) { if (source == null) { return null; } return UserType.valueOf(source); } } 如果还有其他枚举类型需要定义原类型为 String 的 Converter 的时候,我们还得像上面那样定义对应的 Converter 。有了 ConverterFactory 之后,这一切都变得非常简单,因为 UserStatus 、 UserType 等其他枚举类型同属于枚举,所以这个时候我们就可以统一定义一个从 String 到 Enum 的 ConverterFactory ,然后从中获取对应的Converter 进行 convert 操作。 Spring 官方已经为我们实现了这么一个StringToEnumConverterFactory :
-
1 @SuppressWarnings("unchecked") 2 final class StringToEnumConverterFactory implements ConverterFactory
{ 3 4 public Converter getConverter(Class targetType) { 5 return new StringToEnum(targetType); 6 } 7 8 private class StringToEnum implements Converter { 9 10 private final Class enumType;11 12 public StringToEnum(Class enumType) {13 this.enumType = enumType;14 }15 16 public T convert(String source) {17 if (source.length() == 0) {18 // It's an empty enum identifier: reset the enum value to null.19 return null;20 }21 return (T) Enum.valueOf(this.enumType, source.trim());22 }23 }24 25 } 这样,如果是要进行 String 到 UserStatus 的转换,我们就可以通过StringToEnumConverterFactory 实例的 getConverter(UserStatus.class).convert(string)获取到对应的 UserStatus ,如果是要转换为 UserType 的话就是getConverter(UserType.class).convert(string) 。这样就非常方便,可以很好的支持扩展。
- 看GenericConverter 接口的定义
- GenericConverter 接口是所有的 Converter 接口中最灵活也是最复杂的一个类型转换接口。像我们之前介绍的 Converter 接口只支持从一个原类型转换为一个目标类型;ConverterFactory 接口只支持从一个原类型转换为一个目标类型对应的子类型;而GenericConverter 接口支持在多个不同的原类型和目标类型之间进行转换,这也就是GenericConverter 接口灵活和复杂的地方。
- 我们先来看一下 GenericConverter 接口的定义:
-
1 public interface GenericConverter { 2 3 Set
getConvertibleTypes(); 4 5 Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); 6 7 public static final class ConvertiblePair { 8 9 private final Class sourceType;10 11 private final Class targetType;12 13 public ConvertiblePair(Class sourceType, Class targetType) {14 Assert.notNull(sourceType, "Source type must not be null");15 Assert.notNull(targetType, "Target type must not be null");16 this.sourceType = sourceType;17 this.targetType = targetType;18 }19 20 public Class getSourceType() {21 return this.sourceType;22 }23 24 public Class getTargetType() {25 return this.targetType;26 }27 }28 29 } 我们可以看到 GenericConverter 接口中一共定义了两个方法,getConvertibleTypes() 和 convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) 。 getConvertibleTypes 方法用于返回这个GenericConverter 能够转换的原类型和目标类型的这么一个组合; convert 方法则是用于进行类型转换的,我们可以在这个方法里面实现我们自己的转换逻辑。之所以说GenericConverter 是最复杂的是因为它的转换方法 convert 的参数类型 TypeDescriptor是比较复杂的。 TypeDescriptor 对类型 Type 进行了一些封装,包括 value 、 Field 及其对应的真实类型等等,具体的可以查看 API 。
有了上面的基础以后就让我们一起来分析一下Spring为我们提供的GenericConversionService这个类
/** * Spring GenericConversionService 类型转换器核心源代码分析 */public class GenericConversionService implements ConfigurableConversionService { //这个转换器在没有找到对应转化器 并且 源类型与目标类型是同一种类型时使用 private static final GenericConverter NO_OP_CONVERTER = new GenericConverter() { public SetgetConvertibleTypes() { return null; } public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { //可以看到这里并没有做处理,直接将原类型返回 return source; } public String toString() { return "NO_OP"; } }; //这个转换器在没有找到对应转化器 并且 源类型与目标类型不是同一种类型时使用。将抛出异常 private static final GenericConverter NO_MATCH = new GenericConverter() { public Set getConvertibleTypes() { throw new UnsupportedOperationException(); } public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { throw new UnsupportedOperationException(); } public String toString() { return "NO_MATCH"; } }; // converters存放系统所有的类型转换器 // 其结构为 key = sourceType ; value = Map , MatchableConverters>; // 至于MatchableConverters是什么 在后面会讲到 // 整个converters的数据接口大概是这样的 /** * 一个原类型对应一个Map , MatchableConverters>数据结构 * converters * [key: String value = Map , MatchableConverters>] * [key: sourceType1 value = Map , MatchableConverters>] * [key: sourceType2 value = Map , MatchableConverters>] * * * * 一个Map , MatchableConverters>数据结构包含每种目标类型对应的转换器 * Map , MatchableConverters> * [key:int value=MatchableConverters] * [key:targetType1 value=MatchableConverters(1)] * [key:targetType2 value=MatchableConverters(2)] * */ private final Map , Map , MatchableConverters>> converters = new HashMap , Map , MatchableConverters>>(36); // 转换器缓存 // 系统在对某个类型第一次做转换的时候会先去查找匹配的转换器,找到以后会放到这个数据结构之中 提高效率 private final Map converterCache = new ConcurrentHashMap (); //注册类型转换器 Converter接口 这个接口就是像我们上面例子里提到的StringToDateConvert public void addConverter(Converter converter) { //首先根据converter的实例 获取原类型(sourceType) 与 目标类型(targetType)的对象 //关于GenericConverter.ConvertiblePair类 就是对 原类型与目标类型的一种封装 GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converter, Converter.class); if (typeInfo == null) { throw new IllegalArgumentException("Unable to the determine sourceType and targetTypewhich " + "your Converter converts between; declare these generic types."); } //注册转换器 //关于ConverterAdapter的解释 在该类上面有详细的解释 addConverter(new ConverterAdapter(typeInfo, converter)); } public void addConverter(Class sourceType, Class targetType, Converter converter) { GenericConverter.ConvertiblePair typeInfo = new GenericConverter.ConvertiblePair(sourceType, targetType); addConverter(new ConverterAdapter(typeInfo, converter)); } //最终所有的转换器 ConverterFactory,Converter,ConditionalGenericConverter 都会被转换为GenericConverter //对象进行注册 也就是说 GenericConversionService类里的转换器 最终都被包装为GenericConverter对象 public void addConverter(GenericConverter converter) { //拿到GenericConverter所支持的转换类型(GenericConverter.ConvertiblePair)的集合 SetconvertibleTypes = converter.getConvertibleTypes(); //循环拿出没一种 转换类型组合进行注册 for (GenericConverter.ConvertiblePair convertibleType : convertibleTypes) { //将converter对象添加到MatchableConverters对象当中去 getMatchableConverters(convertibleType.getSourceType(), convertibleType.getTargetType()).add(converter); } invalidateCache(); } //注册实现ConverterFactory 接口的转换器 //该方法还是将ConverterFactory的实现类包装为GenericConverter对象 public void addConverterFactory(ConverterFactory converterFactory) { GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converterFactory, ConverterFactory.class); if (typeInfo == null) { throw new IllegalArgumentException("Unable to the determine sourceType and targetRangeType R which " + "your ConverterFactoryconverts between; declare these generic types."); } addConverter(new ConverterFactoryAdapter(typeInfo, converterFactory)); } //移除某种类型的转换器 public void removeConvertible(Class sourceType, Class targetType) { getSourceConverterMap(sourceType).remove(targetType); invalidateCache(); } //判断是否能够转换 public boolean canConvert(Class sourceType, Class targetType) { if (targetType == null) { throw new IllegalArgumentException("The targetType to convert to cannot be null"); } return canConvert(sourceType != null ? TypeDescriptor.valueOf(sourceType) : null, TypeDescriptor.valueOf(targetType)); } //判断是否能够转换 public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) { if (targetType == null) { throw new IllegalArgumentException("The targetType to convert to cannot be null"); } if (sourceType == null) { return true; } GenericConverter converter = getConverter(sourceType, targetType); return (converter != null); } @SuppressWarnings("unchecked") publicT convert(Object source, Class targetType) { if (targetType == null) { throw new IllegalArgumentException("The targetType to convert to cannot be null"); } return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType)); } public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { if (targetType == null) { throw new IllegalArgumentException("The targetType to convert to cannot be null"); } if (sourceType == null) { Assert.isTrue(source == null, "The source must be [null] if sourceType == [null]"); return handleResult(sourceType, targetType, convertNullSource(sourceType, targetType)); } if (source != null && !sourceType.getObjectType().isInstance(source)) { throw new IllegalArgumentException("The source to convert from must be an instance of " + sourceType + "; instead it was a " + source.getClass().getName()); } GenericConverter converter = getConverter(sourceType, targetType); if (converter != null) { Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType); return handleResult(sourceType, targetType, result); } else { return handleConverterNotFound(source, sourceType, targetType); } } /** * Convenience operation for converting a source object to the specified targetType, where the targetType is a descriptor that provides additional conversion context. * Simply delegates to { @link #convert(Object, TypeDescriptor, TypeDescriptor)} and encapsulates the construction of the sourceType descriptor using { @link TypeDescriptor#forObject(Object)}. * @param source the source object * @param targetType the target type * @return the converted value * @throws ConversionException if a conversion exception occurred * @throws IllegalArgumentException if targetType is null * @throws IllegalArgumentException if sourceType is null but source is not null */ public Object convert(Object source, TypeDescriptor targetType) { return convert(source, TypeDescriptor.forObject(source), targetType); } public String toString() { List converterStrings = new ArrayList (); for (Map , MatchableConverters> targetConverters : this.converters.values()) { for (MatchableConverters matchable : targetConverters.values()) { converterStrings.add(matchable.toString()); } } Collections.sort(converterStrings); StringBuilder builder = new StringBuilder(); builder.append("ConversionService converters = ").append("\n"); for (String converterString : converterStrings) { builder.append("\t"); builder.append(converterString); builder.append("\n"); } return builder.toString(); } // subclassing hooks /** * Template method to convert a null source. * Default implementation returns
null
. * Subclasses may override to return custom null objects for specific target types. * @param sourceType the sourceType to convert from * @param targetType the targetType to convert to * @return the converted null object */ protected Object convertNullSource(TypeDescriptor sourceType, TypeDescriptor targetType) { return null; } /** * Hook method to lookup the converter for a given sourceType/targetType pair. * First queries this ConversionService's converter cache. * On a cache miss, then performs an exhaustive search for a matching converter. * If no converter matches, returns the default converter. * Subclasses may override. * @param sourceType the source type to convert from * @param targetType the target type to convert to * @return the generic converter that will perform the conversion, ornull
if no suitable converter was found * @see #getDefaultConverter(TypeDescriptor, TypeDescriptor) */ 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); } else { converter = findConverterForClassPair(sourceType, targetType); if (converter == null) { converter = getDefaultConverter(sourceType, targetType); } if (converter != null) { this.converterCache.put(key, converter); return converter; } else { this.converterCache.put(key, NO_MATCH); return null; } } } /** * Return the default converter if no converter is found for the given sourceType/targetType pair. * Returns a NO_OP Converter if the sourceType is assignable to the targetType. * Returnsnull
otherwise, indicating no suitable converter could be found. * Subclasses may override. * @param sourceType the source type to convert from * @param targetType the target type to convert to * @return the default generic converter that will perform the conversion */ protected GenericConverter getDefaultConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { return (sourceType.isAssignableTo(targetType) ? NO_OP_CONVERTER : null); } //通过反射的到Converter接口的 S与T 封装成GenericConverter.ConvertiblePair对象返回 private GenericConverter.ConvertiblePair getRequiredTypeInfo(Object converter, Class genericIfc) { Class [] args = GenericTypeResolver.resolveTypeArguments(converter.getClass(), genericIfc); return (args != null ? new GenericConverter.ConvertiblePair(args[0], args[1]) : null); } private MatchableConverters getMatchableConverters(Class sourceType, Class targetType) { //通过sourceType(原类型)在converters当中获取Map, MatchableConverters>对象 Map , MatchableConverters> sourceMap = getSourceConverterMap(sourceType); //在Map , MatchableConverters>对象当中根据targetType(目标类型)获取MatchableConverters对象 MatchableConverters matchable = sourceMap.get(targetType); if (matchable == null) { matchable = new MatchableConverters(); sourceMap.put(targetType, matchable); } return matchable; } private void invalidateCache() { this.converterCache.clear(); } //通过sourceType(原类型)在converters当中获取Map , MatchableConverters> private Map , MatchableConverters> getSourceConverterMap(Class sourceType) { Map , MatchableConverters> sourceMap = converters.get(sourceType); //为null就创建一个 if (sourceMap == null) { sourceMap = new HashMap , MatchableConverters>(); this.converters.put(sourceType, sourceMap); } return sourceMap; } private GenericConverter findConverterForClassPair(TypeDescriptor sourceType, TypeDescriptor targetType) { Class sourceObjectType = sourceType.getObjectType(); if (sourceObjectType.isInterface()) { LinkedList > classQueue = new LinkedList >(); classQueue.addFirst(sourceObjectType); while (!classQueue.isEmpty()) { Class currentClass = classQueue.removeLast(); Map , MatchableConverters> converters = getTargetConvertersForSource(currentClass); GenericConverter converter = getMatchingConverterForTarget(sourceType, targetType, converters); if (converter != null) { return converter; } Class [] interfaces = currentClass.getInterfaces(); for (Class ifc : interfaces) { classQueue.addFirst(ifc); } } Map , MatchableConverters> objectConverters = getTargetConvertersForSource(Object.class); return getMatchingConverterForTarget(sourceType, targetType, objectConverters); } else if (sourceObjectType.isArray()) { LinkedList > classQueue = new LinkedList >(); classQueue.addFirst(sourceObjectType); while (!classQueue.isEmpty()) { Class currentClass = classQueue.removeLast(); Map , MatchableConverters> converters = getTargetConvertersForSource(currentClass); GenericConverter converter = getMatchingConverterForTarget(sourceType, targetType, converters); if (converter != null) { return converter; } Class componentType = ClassUtils.resolvePrimitiveIfNecessary(currentClass.getComponentType()); if (componentType.getSuperclass() != null) { classQueue.addFirst(Array.newInstance(componentType.getSuperclass(), 0).getClass()); } else if (componentType.isInterface()) { classQueue.addFirst(Object[].class); } } return null; } else { HashSet > interfaces = new LinkedHashSet >(); LinkedList > classQueue = new LinkedList >(); classQueue.addFirst(sourceObjectType); while (!classQueue.isEmpty()) { Class currentClass = classQueue.removeLast(); Map , MatchableConverters> converters = getTargetConvertersForSource(currentClass); GenericConverter converter = getMatchingConverterForTarget(sourceType, targetType, converters); if (converter != null) { return converter; } Class superClass = currentClass.getSuperclass(); if (superClass != null && superClass != Object.class) { classQueue.addFirst(superClass); } for (Class interfaceType : currentClass.getInterfaces()) { addInterfaceHierarchy(interfaceType, interfaces); } } for (Class interfaceType : interfaces) { Map , MatchableConverters> converters = getTargetConvertersForSource(interfaceType); GenericConverter converter = getMatchingConverterForTarget(sourceType, targetType, converters); if (converter != null) { return converter; } } Map , MatchableConverters> objectConverters = getTargetConvertersForSource(Object.class); return getMatchingConverterForTarget(sourceType, targetType, objectConverters); } } private Map , MatchableConverters> getTargetConvertersForSource(Class sourceType) { Map , MatchableConverters> converters = this.converters.get(sourceType); if (converters == null) { converters = Collections.emptyMap(); } return converters; } private GenericConverter getMatchingConverterForTarget(TypeDescriptor sourceType, TypeDescriptor targetType, Map , MatchableConverters> converters) { Class targetObjectType = targetType.getObjectType(); if (targetObjectType.isInterface()) { LinkedList > classQueue = new LinkedList >(); classQueue.addFirst(targetObjectType); while (!classQueue.isEmpty()) { Class currentClass = classQueue.removeLast(); MatchableConverters matchable = converters.get(currentClass); GenericConverter converter = matchConverter(matchable, sourceType, targetType); if (converter != null) { return converter; } Class [] interfaces = currentClass.getInterfaces(); for (Class ifc : interfaces) { classQueue.addFirst(ifc); } } return matchConverter(converters.get(Object.class), sourceType, targetType); } else if (targetObjectType.isArray()) { LinkedList > classQueue = new LinkedList >(); classQueue.addFirst(targetObjectType); while (!classQueue.isEmpty()) { Class currentClass = classQueue.removeLast(); MatchableConverters matchable = converters.get(currentClass); GenericConverter converter = matchConverter(matchable, sourceType, targetType); if (converter != null) { return converter; } Class componentType = ClassUtils.resolvePrimitiveIfNecessary(currentClass.getComponentType()); if (componentType.getSuperclass() != null) { classQueue.addFirst(Array.newInstance(componentType.getSuperclass(), 0).getClass()); } else if (componentType.isInterface()) { classQueue.addFirst(Object[].class); } } return null; } else { Set > interfaces = new LinkedHashSet >(); LinkedList > classQueue = new LinkedList >(); classQueue.addFirst(targetObjectType); while (!classQueue.isEmpty()) { Class currentClass = classQueue.removeLast(); MatchableConverters matchable = converters.get(currentClass); GenericConverter converter = matchConverter(matchable, sourceType, targetType); if (converter != null) { return converter; } Class superClass = currentClass.getSuperclass(); if (superClass != null && superClass != Object.class) { classQueue.addFirst(superClass); } for (Class interfaceType : currentClass.getInterfaces()) { addInterfaceHierarchy(interfaceType, interfaces); } } for (Class interfaceType : interfaces) { MatchableConverters matchable = converters.get(interfaceType); GenericConverter converter = matchConverter(matchable, sourceType, targetType); if (converter != null) { return converter; } } return matchConverter(converters.get(Object.class), sourceType, targetType); } } private void addInterfaceHierarchy(Class interfaceType, Set > interfaces) { interfaces.add(interfaceType); for (Class inheritedInterface : interfaceType.getInterfaces()) { addInterfaceHierarchy(inheritedInterface, interfaces); } } private GenericConverter matchConverter( MatchableConverters matchable, TypeDescriptor sourceFieldType, TypeDescriptor targetFieldType) { if (matchable == null) { return null; } return matchable.matchConverter(sourceFieldType, targetFieldType); } private Object handleConverterNotFound(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { if (source == null) { assertNotPrimitiveTargetType(sourceType, targetType); return source; } else if (sourceType.isAssignableTo(targetType) && targetType.getObjectType().isInstance(source)) { return source; } else { throw new ConverterNotFoundException(sourceType, targetType); } } private Object handleResult(TypeDescriptor sourceType, TypeDescriptor targetType, Object result) { if (result == null) { assertNotPrimitiveTargetType(sourceType, targetType); } return result; } private void assertNotPrimitiveTargetType(TypeDescriptor sourceType, TypeDescriptor targetType) { if (targetType.isPrimitive()) { throw new ConversionFailedException(sourceType, targetType, null, new IllegalArgumentException("A null value cannot be assigned to a primitive type")); } } //包装我们的Converter类 将我们的Converter类的实例 包装成为GenericConverter对象 以便统一注册 @SuppressWarnings("unchecked") private final class ConverterAdapter implements GenericConverter { //原类型(sourceType) 与 目标类型(targetType) private final ConvertiblePair typeInfo; //我们实现Converter 接口的转换器 private final Converter