引子
创建Person类:
@Data
@Component
public class Person {
@Value("wj")
private String name;
@Value("20")
private Integer age;
@Value("2020/10/10 12:30:30")
private Date birth;
@Value("java,html")
private String[] subject;
@Value("C:\\SoftPlugin.dll")
private File file;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", birth=" + birth +
", subject=" + Arrays.toString(subject) +
", file=" + file +
'}';
}
}
创建配置类:
@ComponentScan("com.wj.spring2")
@Configuration
public class MainConfig {
}
main方法:
public static void main(String[] args){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
运行结果:
控制台最后打印了Person类的相关属性,发现@Value中的配置的内容会被解析到对应的字段上,String类型的name被设置进去还说的通,因为@Value中写的内容本来就是String类型的,那么为什么Integer、Date、String[]、File类型的字段,只要符合一定的格式,就能被正确解析,Spring底层是怎么做的,用了什么组件?我怎么对类型转换进行扩展?
留着问题慢慢往下看!!!
Spring类型转换的几种方式
PropertyEditor
PropertyEditor:属性编辑器,java.beans
包下面的,它规定了将外部设置值转换为内部JavaBean属性值的转换接口方法。PropertyEditor主要的接口方法说明如下:
- Object getValue():返回属性的当前值。基本类型被封装成对应的包装类实例;
- void setValue(Object newValue):设置属性的值,基本类型以包装类传入(自动装箱);
- String getAsText():将属性对象用一个字符串表示,以便外部的属性编辑器能以可视化的方式显示。缺省返回null,表示该属性不能以字符串表示;
- void setAsText(String text):用一个字符串去更新属性的内部值,这个字符串一般从外部属性编辑器传入;
- String[] getTags():返回表示有效属性值的字符串数组(如boolean属性对应的有效Tag为true和false),以便属性编辑器能以下拉框的方式显示出来。缺省返回null,表示属性没有匹配的字符值有限集合;
- String getJavaInitializationString():为属性提供一个表示初始值的字符串,属性编辑器以此值作为属性的默认值。
- void addPropertyChangeListener(PropertyChangeListener listener):添加属性改变监听器
- void removePropertyChangeListener(PropertyChangeListener listener): 移除属性改变监听器
Java为PropertyEditor提供了一个方便的实现类:PropertyEditorSupport,该类实现了PropertyEditor接口并提供默认实现,一般情况下,用户可以通过扩展这个方便类设计自己的属性编辑器。
下面我们简单实现一个属性编辑器,用于将String
类型转换成Date
类型:
public class DatePropertyEditor extends PropertyEditorSupport {
private static final String[] parsePatterns = {"yyyy-MM-dd","yyyy年MM月dd日",
"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd",
"yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyyMMdd"};
@Override
public void setAsText(String text) throws IllegalArgumentException {
try {
//org.apache.commons.lang3.time.DateUtils
Date date = DateUtils.parseDate(text, parsePatterns);
setValue(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
测试:
public static void main(String[] args) {
PropertyEditor editor = new DatePropertyEditor();
editor.setAsText("2021-10-10 12:00:01");
System.out.println(editor.getValue());
}
输出结果:
现在我们如果将前面Person类中的Date值改掉:
@Value("2020-10-10 12:30:30")
private Date birth;
再运行就会报错:
为什么会出现这种情况?这个我们稍后解释。
但是为了解决这种问题,spring给我们提供了一种增加自定义PropertyEditor
的方式:在配置类中增加如下bean
@Bean
CustomEditorConfigurer customEditorConfigurer() {
CustomEditorConfigurer configurer = new CustomEditorConfigurer();
Map<Class<?>, Class<? extends PropertyEditor>> customEditorsMap = new HashMap<>();
//表示DatePropertyEditor可以将String类型转换成Date类型
//spring中,如果发现输入类型是String,输出类型是Date,那么就会使用该PropertyEditor
customEditorsMap.put(Date.class, DatePropertyEditor.class);
configurer.setCustomEditors(customEditorsMap);
return configurer;
}
再运行就运行成功了:
这说明我们自定义的属性编辑器生效了,把字符串转换成Date类型。
但是,jdk提供的PropertyEditor
有很大的局限性,只能将String类型转换成其他类型,但是对于Object转成Object类型就没有办法了,所以Spring提供了强大的类型转换服务ConversionService
供我们使用。
三种转换器
Converter<S, T> (1:1转换)
接口源码:
@FunctionalInterface
public interface Converter<S, T> {
/**
* 转换方法:将S类型转换成T
*/
@Nullable
T convert(S source);
/**
* 支持链式调用
*/
default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after) {
Assert.notNull(after, "After Converter must not be null");
return (S s) -> {
T initialResult = convert(s);
return (initialResult != null ? after.convert(initialResult) : null);
};
}
}
Converter
接口用于将S类型转换成T类型。
下面对它进行测试:我们可以用Spring提供的StringToBooleanConverter
,可以将String转换成Boolean类型。
ConversionService conversionService = new DefaultConversionService();
Boolean flag = conversionService.convert("1", Boolean.class);
System.out.println(flag);
测试结果:
???不是说Converter吗,怎么用的是ConversionService
?
其实我们创建了一个DefaultConversionService
,它内部创建了很多Converter的实现。然后我们调用conversionService.convert
时,它内部去匹配Converter
,然后调用Converter
的convert
方法,这个后面再说。
ConverterFactory<S, R> (1:N转换)
源码如下:
public interface ConverterFactory<S, R> {
//R 的子类都可以统一由这个 ConverterFactory 进行转换。
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
测试:
System.out.println(conversionService.convert(1.2, Long.class));
System.out.println(conversionService.convert(1.2, Double.class));
这里,conversionService底层使用了NumberToNumberConverterFactory
类去转换:
final class NumberToNumberConverterFactory implements ConverterFactory<Number, Number>, ConditionalConverter {
@Override
public <T extends Number> Converter<Number, T> getConverter(Class<T> targetType) {
return new NumberToNumber<>(targetType);
}
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return !sourceType.equals(targetType);
}
private static final class NumberToNumber<T extends Number> implements Converter<Number, T> {
private final Class<T> targetType;
NumberToNumber(Class<T> targetType) {
this.targetType = targetType;
}
@Override
public T convert(Number source) {
//NumberUtils.convertNumberToTargetClass方法去转换
return NumberUtils.convertNumberToTargetClass(source, this.targetType);
}
}
}
GenericConverter (N:N转换)
GenericConverter
一般都是和ConditionalConverter
一起使用的。
源码如下:
public interface GenericConverter {
//获取当前GenericConverter的source==>target的转换对
@Nullable
Set<ConvertiblePair> getConvertibleTypes();
@Nullable
Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
/**
* 用于存储 sourc===>target 的转换对
*/
final class ConvertiblePair {
private final Class<?> sourceType;
private final Class<?> targetType;
public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
Assert.notNull(sourceType, "Source type must not be null");
Assert.notNull(targetType, "Target type must not be null");
this.sourceType = sourceType;
this.targetType = targetType;
}
//其他方法省略
}
}
public interface ConditionalConverter {
//判断当前converter能否从sourceType转换成targetType
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
//继承了GenericConverter和ConditionalConverter
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}
测试:我们可以通过CollectionToCollectionConverter
将List<String>转换成Set<Integer>:
ConversionService conversionService = new DefaultConversionService();
Set<Integer> set = (Set<Integer>)conversionService.convert(Arrays.asList("1","2","1"),
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)),
TypeDescriptor.collection(Set.class, TypeDescriptor.valueOf(Integer.class)));
System.out.println(set);
运行结果:
CollectionToCollectionConverter
源码说明:
final class CollectionToCollectionConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;
public CollectionToCollectionConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Collection.class, Collection.class));
}
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return ConversionUtils.canConvertElements(
sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(), this.conversionService);
}
@Override
@Nullable
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
//如果要转换的source为null,则直接返回
if (source == null) {
return null;
}
//将source强转为Collection
Collection<?> sourceCollection = (Collection<?>) source;
// Shortcut if possible...
//判断targetType集合类型与sourceType集合类型是否相同
boolean copyRequired = !targetType.getType().isInstance(source);
if (!copyRequired && sourceCollection.isEmpty()) {
//不用转换,直接返回
return source;
}
TypeDescriptor elementDesc = targetType.getElementTypeDescriptor();
//判断集合元素类型是否设置了,如果没有则为Object类型,且集合类型没有变
if (elementDesc == null && !copyRequired) {
//不用转换,直接返回
return source;
}
// At this point, we need a collection copy in any case, even if just for finding out about element copies...
//创建原集合大小的空集合
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(),
(elementDesc != null ? elementDesc.getType() : null), sourceCollection.size());
//如果没有设置集合元素类型
if (elementDesc == null) {
//直接addAll
target.addAll(sourceCollection);
}
else {
//遍历原集合的所有元素,依次调用conversionService.convert()方法,将sourceElement转换成targetElement
//并将转换结果放到新集合中
for (Object sourceElement : sourceCollection) {
Object targetElement = this.conversionService.convert(sourceElement,
sourceType.elementTypeDescriptor(sourceElement), elementDesc);
target.add(targetElement);
if (sourceElement != targetElement) {
copyRequired = true;
}
}
}
return (copyRequired ? target : source);
}
}
ConversionService
Spring 3.0 提供了三种类型的转换器(Converter
、ConverterFactory
、GenericConverter
),分别用来处理 1:1、1:N、N:N 的类型转换。而ConversionService
是来统一管理所有的类型转换器,负责注册、查找、转换等功能,统一对外提供服务。
查看该类实现:

ConversionService还有一个FormattingConversionService的实现,因为它跟Formatter有关,不再本文讨论之内,这里不再介绍。
-
ConverterRegistry
:转换器注册中心。负责转换器的注册、删除 -
ConversionService
:统一的类型转换服务。属于面向开发者使用的门面接口 -
ConfigurableConversionService
:上两个接口的组合接口 -
GenericConversionService
实现了ConfigurableConversionService
接口,Spring 使用的ConversionService
都是基于这个类的扩展。 -
DefaultConversionService
扩展GenericConversionService
,注册了一批默认的转换器。
GenericConversionService
save
GenericConversionService
实现了 ConversionService
, ConverterRegistry
两个接口的功能,上面提到的 DefaultConversionService
就是基于 GenericConversionService
的扩展,只是注册了一些默认的转换器。
接下来:先介绍GenericConversionService
的增加转换器方法:
//管理所有已经注册在ConversionService的Converter
private final Converters converters = new Converters();
@Override
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?");
}
//将Converter转成Converter的适配器, 该ConverterAdapter实现ConditionalGenericConverter接口(支持N:N转换)
addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));
}
//将明确指定source/target的类型对添加到ConverterRegistry
//允许将 Converter 重用于多个不同的对,而无需为每对创建 Converter 类。
@Override
public <S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter) {
addConverter(new ConverterAdapter(
converter, ResolvableType.forClass(sourceType), ResolvableType.forClass(targetType)));
}
//增加ConverterFactory支持1:N
@Override
public void addConverterFactory(ConverterFactory<?, ?> factory) {
//获取传进来的ConverterFactory的两个泛型类型
ResolvableType[] typeInfo = getRequiredTypeInfo(factory.getClass(), ConverterFactory.class);
if (typeInfo == null && factory instanceof DecoratingProxy) {
typeInfo = getRequiredTypeInfo(((DecoratingProxy) factory).getDecoratedClass(), ConverterFactory.class);
}
if (typeInfo == null) {
throw new IllegalArgumentException("Unable to determine source type <S> and target type <T> for your " +
"ConverterFactory [" + factory.getClass().getName() + "]; does the class parameterize those types?");
}
//将ConverterFactory转成ConverterFactory适配器,该适配器实现ConditionalGenericConverter接口(支持N:N转换)
addConverter(new ConverterFactoryAdapter(factory,
new ConvertiblePair(typeInfo[0].toClass(), typeInfo[1].toClass())));
}
//增加GenericConverter
@Override
public void addConverter(GenericConverter converter) {
this.converters.add(converter);
invalidateCache();
}
可以看到,当增加Converter
或者是ConverterFactory
,ConversionService
都会先创建该转换器的适配器,实现了ConditionalGenericConverter
接口,然后调用addConverter(GenericConverter converter)
方法,确保无论是添加哪种转换器,最终在ConversionService
中注册的都是GenericConverter
,巧妙如斯!!
最终调用this.converters.add(converter);
,converters是GenericConversionService
类的属性,是Converters
类:
private static class Converters {
private final Set<GenericConverter> globalConverters = new CopyOnWriteArraySet<>();
private final Map<ConvertiblePair, ConvertersForPair> converters = new ConcurrentHashMap<>(256);
public void add(GenericConverter converter) {
//获取该converter的source/target类型对
Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
if (convertibleTypes == null) {
Assert.state(converter instanceof ConditionalConverter,
"Only conditional converters may return null convertible types");
//如果类型对为null,添加到globalConverters的集合中
this.globalConverters.add(converter);
}
else {
//遍历convertibleTypes
for (ConvertiblePair convertiblePair : convertibleTypes) {
//getMatchableConverters(convertiblePair)返回ConvertersForPair后,调用它的add方法,增加converter
getMatchableConverters(convertiblePair).add(converter);
}
}
}
//从converters根据convertiblePair获取ConvertersForPair,如果没有就创建一个空的ConvertiblePair---ConvertersForPair键值对
private ConvertersForPair getMatchableConverters(ConvertiblePair convertiblePair) {
return this.converters.computeIfAbsent(convertiblePair, k -> new ConvertersForPair());
}
//其他方法省略。。
再看ConvertersForPair
类:
/**
* Manages converters registered with a specific {@link ConvertiblePair}.
*/
private static class ConvertersForPair {
//真正存放GenericConverter的地方
private final Deque<GenericConverter> converters = new ConcurrentLinkedDeque<>();
//把converter放到队首,所以越后add的GenericConverter越在前面
public void add(GenericConverter converter) {
this.converters.addFirst(converter);
}
//其他方法省略
}
所以最终存储结构是这样:(当然了,这只是简化版的,GenericConversionService
实际上比这个还要复杂一点)
remove
先看GenericConversionService
的removeConvertible
方法:
@Override
public void removeConvertible(Class<?> sourceType, Class<?> targetType) {
//直接调用Converters的remove方法,移除转换类型对
this.converters.remove(sourceType, targetType);
invalidateCache();
}
再看Converters
的remove
方法:直接调用原生的map的remove方法,从map中移除指定的ConvertiblePair
key,这样也就会把该key对应的所有转换器对象移除。
public void remove(Class<?> sourceType, Class<?> targetType) {
this.converters.remove(new ConvertiblePair(sourceType, targetType));
}
canConvert和convert
接下来,重头戏来了。我们先看GenericConversionService
对canConvert
的实现:
//canConvert(@Nullable Class<?> sourceType, Class<?> targetType)调用的是该类的同名重载方法
@Override
public boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
return canConvert((sourceType != null ? TypeDescriptor.valueOf(sourceType) : null),
TypeDescriptor.valueOf(targetType));
}
@Override
public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
//如果sourceType等于null,直接返回true
if (sourceType == null) {
return true;
}
//调用getConverter方法,如果能得到converter,就返回true
GenericConverter converter = getConverter(sourceType, targetType);
return (converter != null);
}
再看getConverter
方法:
private static final GenericConverter NO_MATCH = new NoOpConverter("NO_MATCH");
private static final GenericConverter NO_OP_CONVERTER = new NoOpConverter("NO_OP");
@Nullable
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
//创建了source/target类型对的缓存key,这一次getConverter的结果会被缓存起来,后面再次调用getConverter,直接返回结果,提升查询的速度
ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
//先从缓存中查询缓存key对应的GenericConverter
GenericConverter converter = this.converterCache.get(key);
if (converter != null) {
//不等于null,判断该Converter是否是NO_MATCH,如果是,返回null,否则直接返回converter
//这里处理就有点向redis的缓存穿透问题一样,为不存在的key也设置一个空对象
return (converter != NO_MATCH ? converter : null);
}
//如果converter为null,说明缓存中没有,则是第一次查询,所以直接从Converters中查询
//调用find方法
converter = this.converters.find(sourceType, targetType);
if (converter == null) {
//获取默认的Converter
converter = getDefaultConverter(sourceType, targetType);
}
//不等于null
if (converter != null) {
//缓存这次查询的结果,并返回
this.converterCache.put(key, converter);
return converter;
}
//缓存一个空的converter
this.converterCache.put(key, NO_MATCH);
//返回null
return null;
}
//如果源类型可分配给目标类型,则返回 NO_OP 转换器。
//否则返回 null,表示找不到合适的转换器。
@Nullable
protected GenericConverter getDefaultConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
return (sourceType.isAssignableTo(targetType) ? NO_OP_CONVERTER : null);
}
再看Converters
的find
方法:
@Nullable
public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
// Search the full type hierarchy
//获取source类型的类层级和target类型的类层级
List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());
List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());
//双重for循环遍历
for (Class<?> sourceCandidate : sourceCandidates) {
for (Class<?> targetCandidate : targetCandidates) {
//创建source类型/target类型一个convertiblePair
ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
//调用getRegisteredConverter()查找GenericConverter
GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
if (converter != null) {
//如果找到了,直接返回converter
return converter;
}
}
}
//没有找到,返回null
return null;
}
这里的getClassHierarchy
返回类型的类层级是什么意思?
以Double
类型为例,我来解释一下:
Double
类的继承实现关系如下:
那么Double调用此方法,会返回它自己、它的父类和它的父接口:
最终返回[Class<Double>, Class<Number>, Class<Comparable>, Class<Serialzable>, Class<Object>],不要忘记Object类型,它也是Double的父类。
getClassHierarchy
说完了,那么这里的双重循环是怎么回事?
无论source类型还是taget类型都有类层级,他会每两个一对依次去查。
以Double和Integer为例,查询顺序为:Double<==>Integer 到 Double<==>Number 到 Double<==>Comparable ... Number<==>Integer 到 Number<==>Number ...
以此类推,直至找到对应的类型转换器。
再看Converters
的getRegisteredConverter
方法,看它是怎么找到已经注册的Converter:
//从此方法来看:converters的优先级要比globalConverters高
@Nullable
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
TypeDescriptor targetType, ConvertiblePair convertiblePair) {
// Check specifically registered converters
//直接从map中调用get(convertiblePair),找到convertersForPair
ConvertersForPair convertersForPair = this.converters.get(convertiblePair);
if (convertersForPair != null) {
//从convertersForPair中找到Converter
GenericConverter converter = convertersForPair.getConverter(sourceType, targetType);
if (converter != null) {
//找到,直接返回
return converter;
}
}
// Check ConditionalConverters for a dynamic match
//如果converters的Map集合中找不到,就从globalConverters中找
for (GenericConverter globalConverter : this.globalConverters) {
//依次调用matches方法
if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {
//找到就直接返回
return globalConverter;
}
}
//找不到返回null
return null;
}
再看ConvertersForPair
的getConverter
方法:
private static class ConvertersForPair {
private final Deque<GenericConverter> converters = new ConcurrentLinkedDeque<>();
@Nullable
public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
//遍历队列
for (GenericConverter converter : this.converters) {
//如果converter实现了ConditionalGenericConverter接口们则会调用matches方法,看是否匹配
//如果没有实现,则直接返回
if (!(converter instanceof ConditionalGenericConverter) ||
((ConditionalGenericConverter) converter).matches(sourceType, targetType)) {
return converter;
}
}
return null;
}
//其他方法省略
}
至此GenericConversionService
的getConverter
方法调用结束。
getConverter
流程图如下:图有点乱,毕竟判断比较多,最好还是自己断点走一遍。
canConvert
看完后,再看convert
方法:
@Override
@Nullable
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() + "]");
}
//调用getConvert方法,与上面分析的getConverter方法是同一个方法
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
//调用ConversionUtils的invokeConverter方法
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
//handleResult:处理返回值
return handleResult(sourceType, targetType, result);
}
//调用handleConverterNotFound方法
return handleConverterNotFound(source, sourceType, targetType);
}
先看invokeConverter
方法:方法很简单,就是直接调用找到的GenericConverter
的convert
方法进行类型转换。
@Nullable
public static Object invokeConverter(GenericConverter converter, @Nullable Object source,
TypeDescriptor sourceType, TypeDescriptor targetType) {
try {
return converter.convert(source, sourceType, targetType);
}
catch (ConversionFailedException ex) {
throw ex;
}
catch (Throwable ex) {
throw new ConversionFailedException(sourceType, targetType, source, ex);
}
}
handleResult
方法也容易,就是判断result==null时,看targetType是否是原始类型,如果是,直接抛出异常
@Nullable
private Object handleResult(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType, @Nullable Object result) {
if (result == null) {
assertNotPrimitiveTargetType(sourceType, targetType);
}
//返回最终结果
return result;
}
再看handleConverterNotFound
方法:
@Nullable
private Object handleConverterNotFound(
@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
//判断targetType是否是原始类型
assertNotPrimitiveTargetType(sourceType, targetType);
return null;
}
//当source类型为null或source类型可分配给target类型并且判断给定的source是否是target类型的实例
//如果判断返回true,则直接返回source
if ((sourceType == null || sourceType.isAssignableTo(targetType)) &&
targetType.getObjectType().isInstance(source)) {
return source;
}
//抛出异常
throw new ConverterNotFoundException(sourceType, targetType);
}
至此,GenericConversionService
类基本分析完毕。
DefaultConversionService
接下来看GenericConversionService
类的子类DefaultConversionService
:
//默认的类型转换服务
public class DefaultConversionService extends GenericConversionService {
@Nullable
private static volatile DefaultConversionService sharedInstance;
//构造方法
public DefaultConversionService() {
addDefaultConverters(this);
}
//返回一个共享实例,此外使用双重检查锁保证了sharedInstance实例只会被创建一次
public static ConversionService getSharedInstance() {
DefaultConversionService cs = sharedInstance;
if (cs == null) {
synchronized (DefaultConversionService.class) {
cs = sharedInstance;
if (cs == null) {
//调用构造方法
cs = new DefaultConversionService();
sharedInstance = cs;
}
}
}
return cs;
}
//增加一些默认的转换器
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToTimeZoneConverter());
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
public static void addCollectionConverters(ConverterRegistry converterRegistry) {
ConversionService conversionService = (ConversionService) converterRegistry;
converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
converterRegistry.addConverter(new MapToMapConverter(conversionService));
converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
converterRegistry.addConverter(new StringToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
converterRegistry.addConverter(new StringToCollectionConverter(conversionService));
converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
converterRegistry.addConverter(new StreamConverter(conversionService));
}
private static void addScalarConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharacterConverter());
converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new NumberToCharacterConverter());
converterRegistry.addConverterFactory(new CharacterToNumberFactory());
converterRegistry.addConverter(new StringToBooleanConverter());
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));
converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToLocaleConverter());
converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharsetConverter());
converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCurrencyConverter());
converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToPropertiesConverter());
converterRegistry.addConverter(new PropertiesToStringConverter());
converterRegistry.addConverter(new StringToUUIDConverter());
converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
}
}
可以看到,当我们new出一个DefaultConversionService
实例时,它内部会注册很多的不同功能的Converter
。
Spring中注册自定义类型转换器
关于类型转换服务,在refresh
容器刷新方法的finishBeanFactoryInitialization
这一步中:
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
//初始化ConversionService
//默认情况下是没有ConversionService的
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
刚开始,就会判断是否有conversionService
bean名称的ConversionService.class
,如果有,就创建ConversionService
的实例,并设置到beanFactory
中。
所以如果我们想要在spring中使用ConversionService
,只需要注册到spring容器中就行了。
此外Spring提供了一个ConversionServiceFactoryBean
的FactoryBean
,这样会方便我们的使用:
public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
@Nullable
private Set<?> converters;
@Nullable
private GenericConversionService conversionService;
//此方法用于我们传入自定义的类型转换器
public void setConverters(Set<?> converters) {
this.converters = converters;
}
@Override
public void afterPropertiesSet() {
//调用createConversionService方法
this.conversionService = createConversionService();
//把converters注册到conversionService中
ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
}
//创建一个DefaultConversionService的实例
protected GenericConversionService createConversionService() {
return new DefaultConversionService();
}
@Override
@Nullable
public ConversionService getObject() {
return this.conversionService;
}
@Override
public Class<? extends ConversionService> getObjectType() {
return GenericConversionService.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
下面提供一个配置的示例:
@ComponentScan("com.wj.spring2")
@Configuration
public class MainConfig {
// @Bean
// CustomEditorConfigurer customEditorConfigurer() {
// CustomEditorConfigurer configurer = new CustomEditorConfigurer();
// Map<Class<?>, Class<? extends PropertyEditor>> customEditorsMap = new HashMap<>();
// customEditorsMap.put(Date.class, DatePropertyEditor.class);
// configurer.setCustomEditors(customEditorsMap);
// return configurer;
// }
//注意这里方法名必须是conversionService,因为spring里面规定了,不然不会使用这个conversionService
//此外我们自定义的类型转换器会优先与DefaultConversionService中默认的类型转换器
//前面解释过了,底层维护的是一个队列,并且调用的addFirst方法
@Bean
ConversionServiceFactoryBean conversionService() {
ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
Set<Converter<?, ?>> set = new HashSet<>();
set.add(new StringToDateConverter());
conversionServiceFactoryBean.setConverters(set);
return conversionServiceFactoryBean;
}
private static class StringToDateConverter implements Converter<String, Date>{
private final java.lang.String[] parsePatterns = {"yyyy-MM-dd","yyyy年MM月dd日",
"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd",
"yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyyMMdd"};
@Override
public Date convert(String source) {
try {
return DateUtils.parseDate(source, parsePatterns);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
}
此外,spring还有一种类型转换方式:TypeConverter,将会于下一篇介绍。