写点什么

Spring 类型转换

用户头像
邱学喆
关注
发布于: 2021 年 04 月 14 日
Spring 类型转换

一. 概述

类型转换,就需要清楚在 java 中都有哪些类型。

在 java 体系中,类型有基础类型,也有引用类型;

  • 基础类型 short, int , long, float, double,boolean,byte, char

  • 引用类型 String, LocalDatetime, 自定义类型

基础类型间的转换,直接去对应的引用类型(Short, Integer, Long, Float, Double, Boolean , Byte, Character)提供的方法进行转换。引用类型间的转换比较复杂,而且还牵扯到泛型。

这里我们着重的解读引用类型转换。

二. Java 类类型

我们都知道对象的祖先都是 Object,java 类类型的祖先是 Type,其子类有 WildcardType, ParameterizedType, TypeVariable, GenericArrayType, Class。

2.1 Type 接口

  • TypeVariable:代表的是泛型变量, 如 List<T>, T 就是泛型变量

  • getName 返回的变量标识符,如上例子代码,则返回的是"T1"

  • getBounds 返回的 extends 后面接着的类型数组,如上例子,由于没有 extends,则返回的是 Object。

  • WildcardType:代表的是通配符类型,List<? extends ReferenceType>, ? extends ReferenceType 就是通配符类型

  • getUpperBounds 返回的 extends 后面的类型,如果没有,则返回的是 Object.

  • getLowerBounds 返回的 super 下界的类型数组

  • GenericArrayType: 代表是数组 Type 类型,type 可以 ParameterizedType 类型也是是 TypeVariable 类型。如,List<List<String>[]>

  • getGenericComponentType 返回的元素类型,如上例子,返回的是 List<String> 是 ParameterizedType 类型。

  • ParameterizedType: 代表的是参数类型, List<T> , <T>就是参数类型

  • getActualTypeArguments 返回的是类型参数,如上例字,则返回的是 T,为 TypeVariable 类型

  • getRawType 返回的是原生类型,一般是 Class,如例子,则返回的 List 类型

  • getOwnerType 如果该类是内部类,则返回的上一级类型。如果不是,则返回的是 null.如上例子,返回是 null.

  • Class: 常规的不用说,大家都知道。值得说的是 String[] 是一个 Class, 并不是 GenericArrayType 类型,通过 getComponentType 可以拿到 String 这个 Class 类型。

这里值得注意的是:TypeVariable, WildcardType, ParameterizedType,GenericArrayType 修饰的泛型类型。我们首先获取一个类型,主要入口是 Class, 接着查看这个 Class 的泛型信息,主要来自于保存到 Class 类中 genericInfo 字段。Field 类以及 Method 类的泛型信息都保存到 genericInfo 字段

2.2 引用对象的类型

引用对象的类型,归根于类的定义,如下代码,基本涵盖了 Type 下所有不同的子类型

class A{}class B<T,R,Q,P>{}class C<T>{}class D{}class ClassDecl<T1,T2 extends A> extends B<C<? extends A>,C<? super A>,C<Integer>[],T1>{} 
复制代码


//类型参数只能是变量类型TypeVariable<Class<T>>[] typeVariables = ClassDecl.class.getTypeParameters();//获取父类的只能是ParameterizedType类型或者是Class类型Type parameterizedType = ClassDecl.class.getGenericSuperclass();//父类的参数类型可以有变量类型,参数类型,通配符类型parameterizedType =(ParameterizedType) parameterizedType;//如例子,//types[0] = C<? extends A> 为ParameterizedType类型//types[1] = C<? super A> 为ParameterizedType类型//types[2] = C<Integer>[] 为GeneraticArrayType//types[3] = T1 为TypeVariable类型Type[] types = parameterizedType.getActualTypeArguments();//? extends A 为WildcardType类型WildcardType wildcardType = ((ParameterizedType)types[0]).getActualTypeArguments()[0];
复制代码

疑问: 代码如下,为什么”场景一“编译不通过,而”场景二“编译确实通过?

//场景一:public class MainEntry{  	class B{}  public static void main(String [] args){    List<String> source = new ArrayList<>();    source.add("sdf");    List<B> target = (List<B>)source;  }}场景二:public class MainEntry{  	class B{}  public static void main(String [] args){    List<String> source = new ArrayList<>();    source.add("sdf");    List<B> target = (List<B>)convert(source);  }  public static Object convert(Object source){    return source;  }}
复制代码

解答:场景一之所以编译不通过,是由于编译过程中,进行了类型校验。而场景二编译通过,是由于调用 convert 方法时,将 source 的泛型擦除了,变成了 Object。接着强转得到 target,然而 target 的元素值的类型却不是 B,而是 String 类型。这是 java 泛型的实现方式导致的,也是一直被诟病。

从拥有参数类型的类型,即携带泛型的类型,例如 ArrayList 类,我们根据这个创建对应的对象时,虽然在"<>"表明了泛型类型,然而通过类进行反射是获取不到该泛型的真实类型,而是统一都是 Object。所以有关这个场景下的泛型转换都会有问题。如场景二所见。

所以 spring 中的类型转换针对此场景也会有所问题。

2.3 Method 中类型

方法中的类型,有返回类型,也有参数类型。


class A{}class B<T>{}interface I<T>{  A methodReturnClass(A a);  B<A> methodReturnClass_(B<A> a);  B<? extends A> methodReturnUpperBound(B<? extends A> a);  B<? super A> methodReturnLowerBound(B<? super A> a);  T methodReturnTypeVariable(T a);  <R> R methodReturnTypeVariable_(R a);  B<A>[] methodReturnGeneraticArrayType(B<A>[] a);}
复制代码

我们通过 Class 反射机制,获取到对应的方法 Method 对象,

  • getGenericReturnType 方法 返回类型 Type。

  • getGenericParameterTypes 方法 返回类型 Type 数组

2.4 Field 类型

Field 的定义

private A a;private B<A> b;B<? extends A> c;B<? super A> d;T e;B<A>[] f;static class A{}static class B<T>{}
复制代码

我们通过 Class 反射机制,可以获取到 Field 对象列表。但值得注意的是有关 Field 列表的获取,有两种方式,一个方式是只获取公开的属性字段,getFields()。另外一种方式是获取同时包含私有的属性字段,getDeclaredFields()。

接着调用 getGenericType 可以获取到对应字段的 Type 类型。

三. spring 关键类

在 spring 中,对 java 类类型进行了封装。

3.1 ResolvableType

根据类名,它是有关对 Type 接口进行了封装,提供相关方法去做类型的匹配,以及切换。

  • 属性

/** * type的类型 */private final Type type;/* * type的提供对象 */@Nullableprivate final TypeProvider typeProvider;/* * 专门用来解析TypeVariable类型变成ResolvableType */@Nullableprivate final VariableResolver variableResolver;/* * 元素类型 ReolvableType */@Nullableprivate final ResolvableType componentType;/** * hash值 */@Nullableprivate final Integer hash;/* * 解析过后的原生class, 例如:  * ParameterizedType -> List<String>,则resolved为List. * TypeVariable -> T , 则resolved为null. * GenericArrayType -> List<String>[], 则为List * WildcardType -> <?> ,则为null, * WildcardType -> <? super A&B> ,则为A */@Nullableprivate Class<?> resolved;/* * 父类的类型 */@Nullableprivate volatile ResolvableType superType;
@Nullableprivate volatile ResolvableType[] interfaces;/* * 保存着泛型信息列表 * ParameterizedType -> List<String>,则generics为Resolable.forClass(String.class). * ParameterizedType -> List<?>,则null. * TypeVariable -> T , 则为null. * GenericArrayType -> List<String>[], 则为null,注意,这个需从componentType去获取 * WildcardType -> <?> ,则为null, * WildcardType -> <? super A&B> ,则为A */@Nullableprivate volatile ResolvableType[] generics;
复制代码

我们重点介绍一下 variableResolver 属性,该属性是专门负责解析变量类型的解析器。该对象是 VariableResolver 接口的实现类。默认情况下,是 DefaultVariableResolver 类,解析出来的是 null。 然而还有两个比较特殊的两个实现类,TypeVariableMapVariableResolver 和 TypeVariablesVariableResolver。两个子类的实现逻辑差不多,都是从映射里找到对应的类型,只是存储的结构不一样而已。之所以存在,是为了针对带有泛型的类,需要明确指定的参数类型对象。例如我们都知道 List 类,通过 ResolvableType 去解析 List 类时,我们是不知道其泛型的具体的参数类型是什么。如果我们想指定 List 的泛型类型是 String,可以通过 ResolvableType.forClassWithGenerics(List.class,String.class)去指定类型。其内部的原理就是创建一个 TypeVariablesVariableResolver 对象,从而进行针对该变量进行指定类型。


  • 方法

/** * 类型判断相关的方法,判断参数中的类型是不是相同的类型 */public boolean isInstance(@Nullable Object obj){}public boolean isAssignableFrom(Class<?> other){}public boolean isAssignableFrom(ResolvableType other)
复制代码

前面的三个方法最终会调用下面的方法。该方法会精确的对类型进行严格的判断,包含泛型的类型的判断。

例如, List<Object> != List<String>, List<String> == ArrayList<String>。

/** * matchedBefore存放之前比对过相等的类型,为了避免重复比对相同类型 */private boolean isAssignableFrom(ResolvableType other, @Nullable Map<Type, Type> matchedBefore) {		if (this == NONE || other == NONE) {			return false;		}
// Deal with array by delegating to the component type if (isArray()) { return (other.isArray() && getComponentType().isAssignableFrom(other.getComponentType())); } //是否有缓存过相同类型的类型比对。如果有,则不用继续比对下去,因为之前已经比对过,是相等的。 if (matchedBefore != null && matchedBefore.get(this.type) == other.type) { return true; }
//通配符比对 WildcardBounds ourBounds = WildcardBounds.get(this); WildcardBounds typeBounds = WildcardBounds.get(other); // In the form X is assignable to <? extends Number> //如果有通配符类型,则对其进行判断。并直接返回 if (typeBounds != null) { return (ourBounds != null && ourBounds.isSameKind(typeBounds) && ourBounds.isAssignableFrom(typeBounds.getBounds())); }
// In the form <? extends Number> is assignable to X... if (ourBounds != null) { return ourBounds.isAssignableFrom(other); }
// Main assignability check about to follow boolean exactMatch = (matchedBefore != null); // We're checking nested generic variables now... boolean checkGenerics = true; Class<?> ourResolved = null; //变量类型比对 if (this.type instanceof TypeVariable) { TypeVariable<?> variable = (TypeVariable<?>) this.type; // Try default variable resolution if (this.variableResolver != null) { ResolvableType resolved = this.variableResolver.resolveVariable(variable); if (resolved != null) { ourResolved = resolved.resolve(); } } if (ourResolved == null) { // Try variable resolution against target type if (other.variableResolver != null) { /** * 这里的逻辑,很关键,如果有值,则借用该解析其,解析当前的变量类型。 * 例如: * ResolvableType(List<>).isAssignableFrom(ResolvableType(List<String>) * 返回的是true是相等的类型。 * ResolvableType(List<String>).isAssignableFrom(ResolvableType(List<String>) * 返回的是false,不是相等的类型。 */ ResolvableType resolved = other.variableResolver.resolveVariable(variable); if (resolved != null) { ourResolved = resolved.resolve(); //很关键的代码,如果解析出来的对象不为空,则无需在进行泛型比对了。 checkGenerics = false; } } } if (ourResolved == null) { // Unresolved type variable, potentially nested -> never insist on exact match exactMatch = false; } } if (ourResolved == null) { ourResolved = resolve(Object.class); } Class<?> otherResolved = other.resolve(Object.class);
// We need an exact type match for generics // List<CharSequence> is not assignable from List<String> if (exactMatch ? !ourResolved.equals(otherResolved) : !ClassUtils.isAssignable(ourResolved, otherResolved)) { return false; } //参数类型比对 if (checkGenerics) { // Recursively check each generic ResolvableType[] ourGenerics = getGenerics(); ResolvableType[] typeGenerics = other.as(ourResolved).getGenerics(); if (ourGenerics.length != typeGenerics.length) { return false; } if (matchedBefore == null) { matchedBefore = new IdentityHashMap<>(1); } matchedBefore.put(this.type, other.type); for (int i = 0; i < ourGenerics.length; i++) { if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], matchedBefore)) { return false; } } }
return true; }
复制代码

这个不是常规的类型判断,例如,List.class.isAssignableFrom(ArrayList.class)返回的 true.

如果 List 类包含的泛型是 Object, 而 ArrayList 类包含的泛型是 String. 通过 ResolvableType 封装后,

进行判断,则是不相等的。因为 ResolvableType 需要判断泛型是否是相等的,在这个例子当中,

一个泛型是 String,另外一个是 Object。

/** * 获取到当前类或者父类与之匹配的类型。 */public ResolvableType asCollection() {}public ResolvableType asMap(){}public ResolvableType as(Class<?> type) {		if (this == NONE) {			return NONE;		}		if (ObjectUtils.nullSafeEquals(resolve(), type)) {			return this;		}		for (ResolvableType interfaceType : getInterfaces()) {			ResolvableType interfaceAsType = interfaceType.as(type);			if (interfaceAsType != NONE) {				return interfaceAsType;			}		}		return getSuperType().as(type);}
复制代码

前面的两个方法最终会调用第三个方法,去获取与之匹配的类型。

/* * 获取该类型中的泛型类型 */public ResolvableType getNested(int nestingLevel){}public ResolvableType getNested(int nestingLevel,Map<Integer, Integer> typeIndexesPerLevel){}public ResolvableType getGeneric(@Nullable int... indexes)  
复制代码

例如,List<String> , 那么调用第一个方法,入参为 2 时,则返回的 ResolvableType.forClass(String.class)的类型。

/** * 则是完全比对了 */public boolean equals(Object other)
复制代码

equals 方法与 isAssignableFrom 是不一样的比对。就好比是"ss" == "ss" 与“ss".equals("ss")一样。具体的代码可以去看对应的源码,这里不再把源码展示出来。

3.2 TypeDescriptor

该类是有关 type 的转换的上下文。

  • 属性

/** * 原生类型,如List<String>,则type为List */private final Class<?> type;/** * 解析后的类型,该类在上面有简述,这里不再说明。 */private final ResolvableType resolvableType;/* * 类型中包含的注解。 */  private final AnnotatedElementAdapter annotatedElement;
复制代码

方法就不再这里过多讲解,基本上是调用 ResolvableType 类来实现的;换句话说,是对 ResolvableType 进一步封装。

稍微值得一提的就是 narrow 方法,该单词的意思是缩短,即当前的 ResolvableType 向指定的对象靠近,主要是将其注解信息给到指定的对象。你可以简单理解为基于指定的对象创建 ResolvableType 对象。

private TypeDescriptor narrow(@Nullable Object value, @Nullable TypeDescriptor typeDescriptor) {  if (typeDescriptor != null) {    return typeDescriptor.narrow(value);  }  if (value != null) {    return narrow(value);  }  return null;}public TypeDescriptor narrow(@Nullable Object value) {  if (value == null) {    return this;  }  ResolvableType narrowed = ResolvableType.forType(value.getClass(), getResolvableType());  return new TypeDescriptor(narrowed, value.getClass(), getAnnotations());}
复制代码

四. PropertyEditor

属性编辑器,在平常的 java 开发当中很少用到。该类产生的初中,是为了针对 GUI 设计的。在界面输入的内容进行解析成自己想要的对象。基本上都是对字符串类型的转换。

其主要的方法有三个。

/** * 该方法如同鸡肋,不涉及到类型的转换。默认的是直接赋值。可以覆盖其方法,让其真正的实现类型转换。 */void setValue(Object value);/** * 获取转换后的对象,如果先调用setValue方法,接着再调用该方法,会原封不动的返回,根本没有进行类型转换。 * 除非是覆盖setValue方法。 */Object getValue();/** * 传入字符串,对其进行解析,并转换指定的类型 */void setAsText(String text);
复制代码

我们简单的查看 PropertyEditor 接口的一个实现类。

public class PropertyEditorSupport implements PropertyEditor{	public void setValue(Object value) {    this.value = value;    //...  }  public Object getValue() {    return value;  }}public class BooleanEditor extends PropertyEditorSupport {    public BooleanEditor() {    }    //....
public void setAsText(String var1) throws IllegalArgumentException { if (var1 == null) { this.setValue((Object)null); } else if (this.isValidName(true, var1)) { this.setValue(Boolean.TRUE); } else { if (!this.isValidName(false, var1)) { throw new IllegalArgumentException(var1); }
this.setValue(Boolean.FALSE); }
} private String getValidName(boolean var1) { return var1 ? "True" : "False"; }
private boolean isValidName(boolean var1, String var2) { return this.getValidName(var1).equalsIgnoreCase(var2); }}
复制代码

五. ConversionService

该类是个接口,有关类型转换的主入口。但注意的是,PropertyEditor 并未包含,可以理解 ConversionService 是 PropertyEditor 的补充。毕竟 PropertyEditor 的类型转换有一定的局限性。

1. GenericConverter

讲解该接口时,要先介绍 GenericConverter,该接口主要是针对携带有泛型的类型转换

这个是 spring 自己定义的接口,针对类型转换的类。其更加灵活。

/** * 获取能转换的类型 */Set<ConvertiblePair> getConvertibleTypes();/* * 类型转换接口 */Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
复制代码

GenericConverter 接口还有一个子接口 ConditionalGenericConverter,主要是为了添加一个判断条件

/* * 判断是否支持sourceType类型转向targetType类型 */boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType)
复制代码

简单查看一个实现类。

/** * 有关Collection向String类型转换 */final class CollectionToStringConverter implements ConditionalGenericConverter {
private static final String DELIMITER = ",";
private final ConversionService conversionService;

public CollectionToStringConverter(ConversionService conversionService) { this.conversionService = conversionService; }

@Override public Set<ConvertiblePair> getConvertibleTypes() { return Collections.singleton(new ConvertiblePair(Collection.class, String.class)); }
@Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { //判断是否支持collection类中泛型的类型与targetType类型的转换。 return ConversionUtils.canConvertElements( sourceType.getElementTypeDescriptor(), targetType, this.conversionService); }
@Override @Nullable public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { if (source == null) { return null; } Collection<?> sourceCollection = (Collection<?>) source; if (sourceCollection.isEmpty()) { return ""; } StringBuilder sb = new StringBuilder(); int i = 0; //遍历集合的元素,将元素转换为String类型,且追加到StringBuilder中,最终转换成String for (Object sourceElement : sourceCollection) { if (i > 0) { sb.append(DELIMITER); } Object targetElement = this.conversionService.convert( sourceElement, sourceType.elementTypeDescriptor(sourceElement), targetType); sb.append(targetElement); i++; } return sb.toString(); }}
复制代码

有关有 List<String> => ["a","b","c"] 转换成 String 类型,其转换后的内容为"a,b,c"。

这里有个问题,为什么用 ConversionService 类对类型进行转换,以及类型转换的判断,而不是该类本身就提供类型的判断呢?ConversionService 类的主要作用是什么,在其中充当什么角色?

ConversionService 接口是充当 GenericConverter 接口实现的“门面”(门面模式),既是主入口。

那问题就来了,具体判断两个类型是否能转换,具体的代码会在哪里呢?后面会有讲解。

2. Converter

该接口是主要是针对未携带的类型间转换。

其方法只有一个

T convert(S source)
复制代码

我们简单查看某个实现类。

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. GenericConversionService

该类 ConversionService 接口、ConverterRegistry 接口的主要实现类。

  • ConversionService

/* * 判断是否支持sourceType类型转换成targetType类型 */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)
复制代码
  • 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)
复制代码

GenericConversionService 的主要类图

Converters 类主要是保存 GenericConverter 实现类,提供查找以及添加方法。其中 Converter 接口主要封装成 GenericConverter 的实现类 ConverterAdapter 后在保存。

Converters 类的主要方法:

public void add(GenericConverter converter) {  //如果为空,则将其保存到全局中。  Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();  if (convertibleTypes == null) {    Assert.state(converter instanceof ConditionalConverter,                 "Only conditional converters may return null convertible types");    this.globalConverters.add(converter);  }  else {    for (ConvertiblePair convertiblePair : convertibleTypes) {      ConvertersForPair convertersForPair = getMatchableConverters(convertiblePair);      convertersForPair.add(converter);    }  }}/** * 根据source和target类型来查找对应的GenericConverter。 * 通过遍历类以及层级父类和接口去尽可能匹配找到对应的converters。 */public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {  // Search the full type hierarchy  List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());  List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());  for (Class<?> sourceCandidate : sourceCandidates) {    for (Class<?> targetCandidate : targetCandidates) {      ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);      GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);      if (converter != null) {        return converter;      }    }  }  return null;}
复制代码

GenericConversionService 类的主要方法

public void addConverter(Converter<?, ?> converter) {  //获取converter实现中的泛型类型  ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);  if (typeInfo == null && converter instanceof DecoratingProxy) {    typeInfo = getRequiredTypeInfo(((DecoratingProxy) converter).getDecoratedClass(), Converter.class);  }  if (typeInfo == null) {    throw new IllegalArgumentException("Unable to determine source type <S> and target type <T> for your " +                                       "Converter [" + converter.getClass().getName() + "]; does the class parameterize those types?");  }  addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));}public void addConverter(GenericConverter converter) {  this.converters.add(converter);  invalidateCache();}/** * 判断是否能转换,主要是从Converters查找对应的GenericConverter,如果能找到,说明支持转换;否则不能支持 */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);}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);  }    converter = this.converters.find(sourceType, targetType);  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;}
复制代码

ConverterAdapter 类的主要方法

//创建ConverterAdapter,将sourceType和targetType封装成ConvertiblePair。//typeInfo代表支持从sourceType转换为targetTypepublic 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;}//获取支持从sourceType转换为targetTypepublic Set<ConvertiblePair> getConvertibleTypes() {  return Collections.singleton(this.typeInfo);}
复制代码

上面的类接口以及原理已经讲解的差不多了,现在以一个例子,进行加深:Long[] 转换 List<Date>。

我们找到对应的 ArrayToCollectionConverter 类

//ArrayToCollectionConverterpublic Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {  if (source == null) {    return null;  }  int length = Array.getLength(source);  //elementDesc为TypeDescriptor.valueOf(Date.class)  TypeDescriptor elementDesc = targetType.getElementTypeDescriptor();  //创建Collection的实现类		Collection<Object> target = CollectionFactory.createCollection(targetType.getType(),                                                                   (elementDesc != null ? elementDesc.getType() : null), length);    if (elementDesc == null) {    for (int i = 0; i < length; i++) {      Object sourceElement = Array.get(source, i);      target.add(sourceElement);    }  }  else {    for (int i = 0; i < length; i++) {      Object sourceElement = Array.get(source, i);      //接着Long类型转换Date类型      Object targetElement = this.conversionService.convert(sourceElement,                                                            sourceType.elementTypeDescriptor(sourceElement), elementDesc);      target.add(targetElement);    }  }  return target;}
复制代码

调用 GenericConversionService 类的方法

//GenericConversionServicepublic Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {  Assert.notNull(targetType, "Target type to convert to cannot be null");  if (sourceType == null) {    Assert.isTrue(source == null, "Source must be [null] if source type == [null]");    return handleResult(null, targetType, convertNullSource(null, targetType));  }  if (source != null && !sourceType.getObjectType().isInstance(source)) {    throw new IllegalArgumentException("Source to convert from must be an instance of [" +                                       sourceType + "]; instead it was a [" + source.getClass().getName() + "]");  }  //找到ConverterAdapter类  GenericConverter converter = getConverter(sourceType, targetType);  if (converter != null) {    Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);    return handleResult(sourceType, targetType, result);  }  return handleConverterNotFound(source, sourceType, targetType);}
复制代码

ConverterAdapter 对象中的 converter 是 LongToDateConverter

//ConverterAdapterpublic Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {   if (source == null) {      return convertNullSource(sourceType, targetType);   }   return this.converter.convert(source);}//LongToDateConverterclass LongToDateConverter implements Converter<Long, Date> {  @Override  public Date convert(Long source) {    return new Date(source);  }}
复制代码

六. 运用

上面讲解了有关类型转换的原理以及实现,在 spring 中是如何调用的;主要是有两大部分,一个是针对 BeanWrappeImpl 类,以及 AbstractBeanFactory。结构如下图

AbstractBeanFactory 只是调用 ConversionService,有关 ConversionService 的实现原理以及调用方式在上一章节有结果,这里不再简述。而 PropertyEditorRegistrySupport 同时包含了 PropertyEditor 以及 ConversionService 两个,具体讲解他们俩是如何配合进行类型转换。里面 TypeConverterDelegate 类最重要的方法如下:

public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,                                @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {    //从propertyEditorRegistry中找出合适的PropertyEditor  PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);  //记录ConversionService转换失败的异常  ConversionFailedException conversionAttemptEx = null;    // No custom editor but custom ConversionService specified?  ConversionService conversionService = this.propertyEditorRegistry.getConversionService();  //如果editor不为空,则优先用PropertyEditor编辑器进行类型转换。  //如果为空,则调用ConversionService类检查是否能转换该类型,能则调用类型转换方法,接着直接返回转换后的对象。  if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {    TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);    if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {      try {        return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);      }      catch (ConversionFailedException ex) {        // fallback to default conversion logic below        //转换异常,则会尝试用默认的编辑器去进行转换        conversionAttemptEx = ex;      }    }  }    Object convertedValue = newValue;    // 如果编辑器不为空或者对象并不是目标类型的,会尝试用编辑器或者默认的编辑器进行类型转换。  if (editor != null ||       (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {    if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&        convertedValue instanceof String) {      //如果目标类型不必为空,且是Collection类型,且对象是String类型,      //可以将其对象按照","进行分割转成字符串数组String[]      TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();      if (elementTypeDesc != null) {        Class<?> elementType = elementTypeDesc.getType();        if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {          convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);        }      }    }    if (editor == null) {      //如果为空,则去获取默认的编辑器      editor = findDefaultEditor(requiredType);    }    //调用编辑器进行类型转换    convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);  }   //标准类型转换标志,指不是PropertyEditor转换以及ConversionService转换的过程  boolean standardConversion = false;  //如果目标类型不为空,如果匹配则尝试用标准的类型转换规则进行转换。  if (requiredType != null) {    if (convertedValue != null) {      if (Object.class == requiredType) {        //如果是Object,则直接返回        return (T) convertedValue;      }      else if (requiredType.isArray()) {        //如果是数组类型,则会遍历数组中的元素再次进行类型转换,里面有递归的形式        if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {          //如果对象为字符串类型,且目标类型的元素类型是枚举,则将对象转换成字符串数组          convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);        }        return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());      }      else if (convertedValue instanceof Collection) {        //遍历对象中的元素,再次进行类型转换        convertedValue = convertToTypedCollection(          (Collection<?>) convertedValue, propertyName, requiredType, typeDescriptor);               standardConversion = true;              }      else if (convertedValue instanceof Map) {        //遍历对象中的key,value,再次进行类型转换        convertedValue = convertToTypedMap(          (Map<?, ?>) convertedValue, propertyName, requiredType, typeDescriptor);              standardConversion = true;      }      if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {        //如果已经转换后的对象是数组,且数量只为1,则再次转换成元素中的类型。        //只有当目标类型不是数组类型,在会进入到这个分支。        convertedValue = Array.get(convertedValue, 0);        standardConversion = true;      }      if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {        // We can stringify any primitive value...        return (T) convertedValue.toString();      }      else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {        if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {          try {            //当没有调用conversionService调用异常和目标类型不是接口和枚举类型时,则尝试通过构造函数去转换。                        //转换成功,则直接返回;失败,则继续另外的类型转换            Constructor<T> strCtor = requiredType.getConstructor(String.class);            return BeanUtils.instantiateClass(strCtor, convertedValue);          }          catch (NoSuchMethodException ex) {            // proceed with field lookup            if (logger.isTraceEnabled()) {              logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);            }          }          catch (Exception ex) {            if (logger.isDebugEnabled()) {              logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex);            }          }        }        String trimmedValue = ((String) convertedValue).trim();        if (requiredType.isEnum() && "".equals(trimmedValue)) {          // It's an empty enum identifier: reset the enum value to null.          return null;        }        //尝试转换枚举类型或目标类型的静态属性对象        convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);        standardConversion = true;      }      else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {        convertedValue = NumberUtils.convertNumberToTargetClass(          (Number) convertedValue, (Class<Number>) requiredType);        standardConversion = true;      }    }    else {      // convertedValue == null      if (requiredType == Optional.class) {        convertedValue = Optional.empty();      }    }        if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {      //如果类型不相等,且conversionService转换失败,则直接向上抛出异常      if (conversionAttemptEx != null) {        // Original exception from former ConversionService call above...        throw conversionAttemptEx;      }      else if (conversionService != null && typeDescriptor != null) {					        //如果conversionService进行类型转换过,只是编辑器无法转换成想要的目标类型,则        //尝试用conversionService进行类型转换        TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);        if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {          return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);        }      }            //如果以上两种方式都无效,则组装异常信息,向上抛出去      StringBuilder msg = new StringBuilder();      msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));      msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");      if (propertyName != null) {        msg.append(" for property '").append(propertyName).append("'");      }      if (editor != null) {        msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(          "] returned inappropriate value of type '").append(          ClassUtils.getDescriptiveType(convertedValue)).append("'");        throw new IllegalArgumentException(msg.toString());      }      else {        msg.append(": no matching editors or conversion strategy found");        throw new IllegalStateException(msg.toString());      }    }  }    if (conversionAttemptEx != null) {    if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {      throw conversionAttemptEx;    }    logger.debug("Original ConversionService attempt failed - ignored since " +                 "PropertyEditor based conversion eventually succeeded", conversionAttemptEx);  }      return (T) convertedValue;}
复制代码

里面的条件比较多,有涉及递归的,简单把关键的一些步骤进行梳理成流程图,如下图:

七. 总结

spring 的类型转换,涉及到 java 的各种对象之间的类型转换,其中包含复杂的泛型。spring 对 java 中所有的类型进行了封装成 ResolvableType。类型转换采用了 java 原生 PropertyEditor 机制以及自身设计的一套 GenericConverter 和 Converter 接口进行类型转换。在 spring 内进行类型转换的主要入口为 TypeConverterDelegate 以及 GenericConversionService 两大类。

发布于: 2021 年 04 月 14 日阅读数: 40
用户头像

邱学喆

关注

计算机原理的深度解读,源码分析。 2018.08.26 加入

在IT领域keep Learning。要知其然,也要知其所以然。原理的爱好,源码的阅读。输出我对原理以及源码解读的理解。个人的仓库:https://gitee.com/Michael_Chan

评论

发布
暂无评论
Spring 类型转换