SF 프레임워크 시리즈 5.13-spring-beans-type 변환 TypeConverter를 배워보자

    유형 변환은 기본적으로 데이터를 한 데이터 유형에서 다른 데이터 유형의 값으로 변환하는 것을 의미합니다.

스프링 유형 변환

    Spring에서 유형 변환은 속성에 해당하는 데이터 유형에 따라 (입력 또는 기타 소스로부터) 외부화된 Bean 속성 값을 변환하는 것입니다. 유형 변환은 Spring 컨테이너에서 널리 사용됩니다. 유형 변환이 필요한 애플리케이션 어디에서나 공개 API를 사용할 수도 있습니다.
    Spring의 core.covert 패키지는 일반적인 유형 변환 시스템을 제공합니다. 시스템은 유형 변환 로직을 구현하기 위한 SPI와 런타임 시 유형 변환을 수행하기 위한 API를 정의합니다.

SPI 변환

유형 변환 논리를 구현하는 SPI는 다음과 같습니다.

package org.springframework.core.convert.converter;
public interface Converter<S, T> {
    
    
	T convert(S source);
}

문자열에서 숫자 및 기타 일반적인 유형으로의 변환기를 포함하여 다중 변환기 구현이 core.covert.support 패키지에 제공됩니다. 일반적인 예는 다음과 같습니다.

package org.springframework.core.convert.support;

final class StringToInteger implements Converter<String, Integer> {
    
    

	public Integer convert(String source) {
    
    
		return Integer.valueOf(source);
	}
}

우리는 또한 우리 자신의 변환기를 구현하고 이 인터페이스를 구현할 수도 있습니다.

일반 변환기

복잡한 Converter 구현이 필요한 경우 GenericConverter 인터페이스 사용을 고려하세요. GenericConverter는 Converter보다 더 유연하며 여러 소스 유형과 대상 유형 간의 변환을 지원합니다. 또한 GenericConverter는 변환 논리를 구현할 때 사용할 수 있는 사용 가능한 소스 및 대상 필드 컨텍스트를 제공합니다. 이러한 컨텍스트를 사용하면 필드 주석이나 필드 서명에 선언된 일반 정보에 따라 유형 변환을 수행할 수 있습니다. GenericConverter 인터페이스는 다음과 같이 정의됩니다.

package org.springframework.core.convert.converter;
public interface GenericConverter {
    
    
	// 支持的源烈性->目标类型对(Pair)
	public Set<ConvertiblePair> getConvertibleTypes();
	// 转换处理 TypeDescriptor见下一节
	Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

많은 Spring 변환기는 ArrayToCollectionConverter와 같은 GenericConverter 인터페이스를 구현합니다.
GenericConverter와 Converter의 주요 차이점은 대부분의 Converter는 기본 유형 변환을 제공하고 GenericConverter는 복합 유형 변환을 제공한다는 것입니다.

게다가 Spring은 특정 조건이 충족될 때만 변환기가 실행되도록 설계된 ConditionalGenericConverter도 제공합니다. 예를 들어 대상 필드에 특정 주석이 있는 경우에만 변환기를 실행하거나 대상 클래스에 특정 메서드(예: static valueOf 메서드)가 정의된 경우에만 변환기를 실행합니다.

유형 설명자

org.springframework.core.convert.TypeDescriptor는 Bean 속성 유형을 통일적으로 기술하여 유형 변환이 통일적으로 수행될 수 있도록 하는 클래스이다. 그 기능은 다음과 같습니다.
1. 속성(Property), 필드(Field), 메서드 매개 변수(MethodParameter) 및 확인 가능한 유형(ResolvableType)을 기반으로 TypeDescriptor를 구축합니다. 2.
인스턴스 개체(TypeDescriptor.forObject(Object)를 기반으로 할 수 있습니다. ), 클래스(TypeDescriptor.valueOf (Class<?>)) TypeDescriptor 획득
3. 배열, 조합, Map 및 기타 유형에 대한 요소 유형에 해당하는 TypeDescriptor를 제공합니다.
4. 유형(TypeDescriptor)이 유형인지 여부를 판별할 수 있습니다. array, collection, Map 등
5. 속성 획득 가능 여부 Annotation 있음
6. Annotation 및 Annotation 정보 획득 가능
7. Type의 중첩 수준 개수 획득 가능 예를 들어 중첩 수준 개수 List의 중첩 수준은 1이고 List<List>의 중첩 수준 수는 2입니다.

ResolvableType: getSuperType(), getInterface() 및 getGeneric(int...)(일반 매개변수)에 대한 액세스를 제공하고 궁극적으로 Java.lang .Class로 구문 분석될 수 있는 Java.lang.reflect.Type의 Spring 캡슐화입니다.

전환 서비스

ConversionService 인터페이스는 다음과 같이 정의된 유형 변환 메소드를 정의합니다.

package org.springframework.core.convert;
public interface ConversionService {
    
    
	boolean canConvert(Class<?> sourceType, Class<?> targetType);
	<T> T convert(Object source, Class<T> targetType);
	boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
	Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

기본 구현은 GenericConversionService입니다. Spring 애플리케이션에서는 일반적으로 각 Spring 컨테이너(또는 ApplicationContext)에 대해 하나의 ConversionService 인스턴스가 구성됩니다. Spring은 ConversionService를 얻고 프레임워크가 유형 변환을 수행해야 할 때 이를 사용합니다. 애플리케이션은 또한 이 ConversionService를 빈에 주입하고 직접 호출할 수 있습니다.

클래스 다이어그램

여기에 이미지 설명을 삽입하세요.
유형 변환은 TypeConverterSupport로 집계되는 TypeConverterDelegate에서 구현됩니다. TypeConverterSupport는 추상 클래스이며, 하위 클래스 BeanWrapperImpl은 주로 TypeConverter의 주요 사용 시나리오이기도 한 데이터 바인딩에 사용됩니다(" https://editor.csdn.net/md?articleId=131913078 " 참조).

소스 코드

유형 변환 구현은 TypeConverterDelegate.convertIfNecessary에서 처리되며 다음과 같이 분석을 시작합니다.

TypeConverterDelegate.convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, @Nullable Class 필수Type, @Nullable TypeDescriptor typeDescriptor)

    /**
	 * 转换值到特定属性的数据类型
	 * @param propertyName 属性名
	 * @param oldValue 属性原有的值(通常为null)
	 * @param newValue 待转换数据
	 * @param requiredType 要转换的数据类型
	 * @param typeDescriptor 要转换的数据类型的typeDescriptor
	 * @return 返回转换后的新值
	 */
	@SuppressWarnings("unchecked")
	@Nullable
	public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
    
    

		// 获取属性编辑器(首先按propertyName获取,如果没有再按requiredType获取)
		PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);

		ConversionFailedException conversionAttemptEx = null;

		// 获取用于类型转换的服务(实现是GenericConversionService)
		ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
		if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
    
    
			// 获取newValue的TypeDescriptor
			TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
			if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
    
    
				// 类型可以转换
				try {
    
    
					// 转换
					return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
				}
				catch (ConversionFailedException ex) {
    
    
					// 转换发生异常,记录异常,并继续往下执行
					conversionAttemptEx = ex;
				}
			}
		}

		// 转换变量赋初值为待转换的数据(提醒:待转换的数据在转换过程中可能需要经过多次转换,如:字符串先到字符串数组再转换等)
		Object convertedValue = newValue;

		// Value not of required type?
		if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
    
    
			if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
					convertedValue instanceof String text) {
    
    
				// 获取要转换的数据类型typeDescriptor的ElementTypeDescriptor(数组或集合或Stream的TypeDescriptor)
				TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
				if (elementTypeDesc != null) {
    
    
					Class<?> elementType = elementTypeDesc.getType();
					if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
    
    
						// 把包含有分隔符的数据转换为字符串数组
						convertedValue = StringUtils.commaDelimitedListToStringArray(text);
					}
				}
			}
			if (editor == null) {
    
    
				// 按需要类型取属性编辑器
				editor = findDefaultEditor(requiredType);
			}
			// 使用给定的属性编辑器将值转换为所需的类型
			convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
		}

		boolean standardConversion = false;

		if (requiredType != null) {
    
    
			if (convertedValue != null) {
    
    
				/* 标准类型转换 */
				if (Object.class == requiredType) {
    
    
					// requiredType是Object类型,直接返回
					return (T) convertedValue;
				}
				else if (requiredType.isArray()) {
    
    
					// 数组类型转换
					if (convertedValue instanceof String text && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
    
    
						convertedValue = StringUtils.commaDelimitedListToStringArray(text);
					}
					return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
				}
				else if (convertedValue instanceof Collection<?> coll) {
    
    
					// 集合类型转换
					convertedValue = convertToTypedCollection(coll, propertyName, requiredType, typeDescriptor);
					standardConversion = true;
				}
				else if (convertedValue instanceof Map<?, ?> map) {
    
    
					// Map类型转换
					convertedValue = convertToTypedMap(map, propertyName, requiredType, typeDescriptor);
					standardConversion = true;
				}
				if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
    
    
					// 处理待转换数据是数组类型但只有一个数据的情况
					convertedValue = Array.get(convertedValue, 0);
					standardConversion = true;
				}
				if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
    
    
					// 目标类型是String且convertedValue是基本类型(boolean, byte, * char, short, int, long, float, double, void或包装过的Boolean, Byte, Character, Short, Integer, Long, Float,Double, or Void),直接串化返回
					return (T) convertedValue.toString();
				}
				else if (convertedValue instanceof String text && !requiredType.isInstance(convertedValue)) {
    
    
					/* 待转换值是String,但不能直接实例化为目标类型 */
					if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
    
    
						try {
    
    
							// 前面转换无异常且目标类型非接口非枚举情况下,用字符串的构建器实例化待转换值
							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 = text.trim();
					if (requiredType.isEnum() && trimmedValue.isEmpty()) {
    
    
						// 目标类型为枚举,但待转换值为空返回null
						return null;
					}
					// 把待转换值转换为枚举类型
					convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
					standardConversion = true;
				}
				else if (convertedValue instanceof Number num && Number.class.isAssignableFrom(requiredType)) {
    
    
					// 待转换值为Number类型(包括Byte、Short、Integer、Long、BigInteger、Float、Double、BigDecimal)且目标类型也是,把待转换值转换为Number
					convertedValue = NumberUtils.convertNumberToTargetClass(num, (Class<Number>) requiredType);
					standardConversion = true;
				}
			}
			else {
    
    
				// convertedValue == null,如果requiredType是Optional,给空值
				if (requiredType == Optional.class) {
    
    
					convertedValue = Optional.empty();
				}
			}

			if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
    
    
				// convertedValue不能按目标类型赋值
				if (conversionAttemptEx != null) {
    
    
					// 有异常直接抛出
					throw conversionAttemptEx;
				}
				else if (conversionService != null && typeDescriptor != null) {
    
    
					// 前面用属性编辑器生成convertedValue不满足目标类型赋值(此种情况下没有用conversionService转换过),且满足类型可转换,则用conversionService转换返回
					TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
					if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
    
    
						return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
					}
				}

				// 定义异常消息,抛出异常(IllegalArgumentException/IllegalStateException)
				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;
	}

GenericConversionService.convert(@Nullable 개체 소스, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType)

유형 변환.

	@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() + "]");
		}
		// 获取转换器,不同类型的转换器是不同的 注1
		GenericConverter converter = getConverter(sourceType, targetType);
		if (converter != null) {
    
    
			// 实际就是执行converter.convert(source, sourceType, targetType),ConversionUtils对其封装(目的是异常处理)
			Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
			// 返回result (内部处理只针对result==null情况)
			return handleResult(sourceType, targetType, result);
		}
		// 未找到对应转换器的处理
		return handleConverterNotFound(source, sourceType, targetType);
	}

참고 1: 유형 변환기 구현은 아래 그림에 표시됩니다.
여기에 이미지 설명을 삽입하세요.

적용 사례

@Component
public class DemoTypeConverter {
    
    
	
	public void demo() {
    
    
		// 简单转换
    	DefaultConversionService  defaultConversionService = new DefaultConversionService();
        System.out.println(defaultConversionService.convert(" 20", Integer.class));
        System.out.println(defaultConversionService.convert(20.1, String.class));

        /* 基于bean转换 */
        Address addr=new Address();
		addr.setProvince("四川");
		addr.setCity("成都");
		addr.setCounty("高新区");
		addr.setDesc("学习大道168");
		Map<String, Object> input = new HashMap<>();
        input.put("surname", "wang");
        input.put("name", "wang");
        input.put("age", 20);
        input.put("address", addr);

		Driver driver=new Driver();
		
		// 生成driver的封装类,偏于操作相关属性
        BeanWrapperImpl beanWrapper=new BeanWrapperImpl(driver);
		beanWrapper.setExtractOldValueForEditor(true);
		beanWrapper.setAutoGrowNestedPaths(true);
		beanWrapper.setAutoGrowCollectionLimit(10000);

		// 值按指定属性类型转换 (自动trim空格)
        System.out.println(beanWrapper.convertForProperty(30, "age"));
        System.out.println(beanWrapper.convertForProperty("30", "age"));
        System.out.println(beanWrapper.convertForProperty(" 30", "age"));
//        System.out.println(beanWrapper.convertForProperty("aa", "age")); // 会抛异常
        
        // 转换input值到driver对应属性
        input.forEach((key,value)->{
    
    
        	beanWrapper.setPropertyValue(key, beanWrapper.convertForProperty(value,key));
        	// 打印driver属性值
        	System.out.println(key+":"+beanWrapper.getPropertyValue(key));        	
        });		
	}
}

Supongo que te gusta

Origin blog.csdn.net/davidwkx/article/details/131913840
Recomendado
Clasificación