zoukankan      html  css  js  c++  java
  • Spring源码分析:类型转换(一)之PropertyEditor和ConversionService

    引子

    创建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);
    	}
    

    运行结果:

    image-20211216170253053

    控制台最后打印了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());
    	}
    

    输出结果:

    image-20211216173139191

    现在我们如果将前面Person类中的Date值改掉:

    	@Value("2020-10-10 12:30:30")
    	private Date birth;
    

    再运行就会报错:

    image-20211216174122560

    为什么会出现这种情况?这个我们稍后解释。

    但是为了解决这种问题,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;
    	}
    
    

    再运行就运行成功了:

    image-20211216174510034

    这说明我们自定义的属性编辑器生效了,把字符串转换成Date类型。

    但是,jdk提供的PropertyEditor有很大的局限性,只能将String类型转换成其他类型,但是对于Object转成Object类型就没有办法了,所以Spring提供了强大的类型转换服务ConversionService供我们使用。

    三种转换器

    image-20211217094318092

    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);
    

    测试结果:

    image-20211217104853560

    ???不是说Converter吗,怎么用的是ConversionService

    其实我们创建了一个DefaultConversionService,它内部创建了很多Converter的实现。然后我们调用conversionService.convert时,它内部去匹配Converter,然后调用Converterconvert方法,这个后面再说。

    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));
    

    image-20211217105729487

    这里,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);
    

    运行结果:

    image-20211217120028090

    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 提供了三种类型的转换器(ConverterConverterFactoryGenericConverter),分别用来处理 1:1、1:N、N:N 的类型转换。而ConversionService是来统一管理所有的类型转换器,负责注册、查找、转换等功能,统一对外提供服务。

    查看该类实现:

    image-20211217140232244

    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实际上比这个还要复杂一点)

    spring类型转换

    remove

    先看GenericConversionServiceremoveConvertible方法:

    	@Override
    	public void removeConvertible(Class<?> sourceType, Class<?> targetType) {
                    //直接调用Converters的remove方法,移除转换类型对
    		this.converters.remove(sourceType, targetType);
    		invalidateCache();
    	}
    

    再看Convertersremove方法:直接调用原生的map的remove方法,从map中移除指定的ConvertiblePair key,这样也就会把该key对应的所有转换器对象移除。

    		public void remove(Class<?> sourceType, Class<?> targetType) {
    			this.converters.remove(new ConvertiblePair(sourceType, targetType));
    		}
    

    canConvert和convert

    接下来,重头戏来了。我们先看GenericConversionServicecanConvert的实现:

    	//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);
    	}
    

    再看Convertersfind方法:

    		@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类的继承实现关系如下:

    image-20211217155026367

    那么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 ...

    以此类推,直至找到对应的类型转换器。

    再看ConvertersgetRegisteredConverter方法,看它是怎么找到已经注册的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;
    		}
    

    再看ConvertersForPairgetConverter方法:

    	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;
    		}
            //其他方法省略
    	}
    

    至此GenericConversionServicegetConverter方法调用结束。

    getConverter流程图如下:图有点乱,毕竟判断比较多,最好还是自己断点走一遍。

    GenericConversionService流程

    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方法:方法很简单,就是直接调用找到的GenericConverterconvert方法进行类型转换。

    	@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));
    		}
    

    刚开始,就会判断是否有conversionServicebean名称的ConversionService.class,如果有,就创建ConversionService的实例,并设置到beanFactory中。

    所以如果我们想要在spring中使用ConversionService,只需要注册到spring容器中就行了。

    此外Spring提供了一个ConversionServiceFactoryBeanFactoryBean,这样会方便我们的使用:

    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,将会于下一篇介绍。

  • 相关阅读:
    devops之 gitlab-ci + mesos + docker + marathon 持续发布③marathon常用api的使用
    centos7环境下二进制编译安装ffmpeg
    jenkins2.236 + sonarqube7.6 + sonar-scanner3.3的集成配置和生产环境使用示例
    zabbix添加top10内存和cpu资源占用情况
    Marathon基于有认证的harbor仓库创建应用
    devops之 gitlab-ci + mesos + docker + marathon 持续发布③marathon 结合 gitlab-ci的CICD持续发布
    docker拉取指定版本的centos和python镜像
    devops之 gitlab-ci + mesos + docker + marathon 持续发布②安装marathon
    devops之 gitlab-ci + mesos + docker + marathon 持续发布①mesos集群环境的搭建
    zabbix4.0监控gpu
  • 原文地址:https://www.cnblogs.com/wwjj4811/p/15703405.html
Copyright © 2011-2022 走看看