一. 概述
类型转换,就需要清楚在 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的提供对象
*/
@Nullable
private final TypeProvider typeProvider;
/*
* 专门用来解析TypeVariable类型变成ResolvableType
*/
@Nullable
private final VariableResolver variableResolver;
/*
* 元素类型 ReolvableType
*/
@Nullable
private final ResolvableType componentType;
/**
* hash值
*/
@Nullable
private final Integer hash;
/*
* 解析过后的原生class, 例如:
* ParameterizedType -> List<String>,则resolved为List.
* TypeVariable -> T , 则resolved为null.
* GenericArrayType -> List<String>[], 则为List
* WildcardType -> <?> ,则为null,
* WildcardType -> <? super A&B> ,则为A
*/
@Nullable
private Class<?> resolved;
/*
* 父类的类型
*/
@Nullable
private volatile ResolvableType superType;
@Nullable
private 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
*/
@Nullable
private 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转换为targetType
public ConverterAdapter(Converter<?, ?> converter, ResolvableType sourceType, ResolvableType targetType) {
this.converter = (Converter<Object, Object>) converter;
this.typeInfo = new ConvertiblePair(sourceType.resolve(Object.class), targetType.resolve(Object.class));
this.targetType = targetType;
}
//获取支持从sourceType转换为targetType
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(this.typeInfo);
}
复制代码
上面的类接口以及原理已经讲解的差不多了,现在以一个例子,进行加深:Long[] 转换 List<Date>。
我们找到对应的 ArrayToCollectionConverter 类
//ArrayToCollectionConverter
public 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 类的方法
//GenericConversionService
public 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
//ConverterAdapter
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return convertNullSource(sourceType, targetType);
}
return this.converter.convert(source);
}
//LongToDateConverter
class 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 两大类。
评论