一. 概述
类型转换,就需要清楚在 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 对象,
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
该接口是主要是针对未携带的类型间转换。
其方法只有一个
我们简单查看某个实现类。
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 接口的主要实现类。
/* * 判断是否支持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)
复制代码
/** * 添加类型转换对象。 */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 两大类。
评论