Spring Boot 的类型转换实现

1. 转换的配置

WebFlux转换服务是通过 org.springframework.boot.autoconfigure.web.format.WebConversionService 实现的,他实现在不同类型的属性之间进行转换。

其类图如下图所示
在这里插入图片描述
其提供多种默认类型的转换,开发人员也可以根据自己的需求自定义转换规则。
支持Converter,GenericConverter,Formatter等多种形式的自定义转换,只需要编写自己的实现类并注册到Spring容器中就可以实现注册。
所有的自定义转换最终都会转换成GenericConverter存储到Map中。

在这里插入图片描述
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration.WebFluxConfig#addFormatters

@Override
public void addFormatters(FormatterRegistry registry) {
	for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
		registry.addConverter(converter);
	}
	for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
		registry.addConverter(converter);
	}
	for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
		registry.addFormatter(formatter);
	}
}

1.1 StringToZonedDateTimeConverter

org.springframework.cloud.gateway.support.StringToZonedDateTimeConverter

public class StringToZonedDateTimeConverter implements Converter<String, ZonedDateTime> {

	@Override
	public ZonedDateTime convert(String source) {
		ZonedDateTime dateTime;
		try {
			long epoch = Long.parseLong(source);

			dateTime = Instant.ofEpochMilli(epoch).atOffset(ZoneOffset.ofTotalSeconds(0))
					.toZonedDateTime();
		}
		catch (NumberFormatException e) {
			// try ZonedDateTime instead
			dateTime = ZonedDateTime.parse(source);
		}

		return dateTime;
	}

}

1.2 StringToRedisClientInfoConverter

public class StringToRedisClientInfoConverter implements Converter<String[], List<RedisClientInfo>> {
	/*
	 * (non-Javadoc)
	 * @see org.springframework.core.convert.converter.Converter#convert(Object)
	 */
	@Override
	public List<RedisClientInfo> convert(String[] lines) {
		List<RedisClientInfo> clientInfoList = new ArrayList<>(lines.length);
		for (String line : lines) {
			clientInfoList.add(RedisClientInfoBuilder.fromString(line));
		}
		return clientInfoList;
	}
}

2. 绑定的方式

可以通过创建Binder处理字段绑定逻辑,提供聚合、bean、property维度的类型转换。

以spring-cloud-gateway的route definiton转换成route的逻辑举例:
org.springframework.cloud.gateway.support.ConfigurationUtils#bind(java.lang.Object, java.util.Map<java.lang.String,java.lang.Object>, java.lang.String, java.lang.String, org.springframework.validation.Validator, org.springframework.core.convert.ConversionService)

public static void bind(Object o, Map<String, Object> properties,
	String configurationPropertyName, String bindingName, Validator validator,
		ConversionService conversionService) {
	Object toBind = getTargetObject(o);

	new Binder(
			Collections.singletonList(new MapConfigurationPropertySource(properties)),
			null, conversionService).bind(configurationPropertyName,
					Bindable.ofInstance(toBind));

	if (validator != null) {
		BindingResult errors = new BeanPropertyBindingResult(toBind, bindingName);
		validator.validate(toBind, errors);
		if (errors.hasErrors()) {
			throw new RuntimeException(new BindException(errors));
		}
	}
}

org.springframework.boot.context.properties.bind.Binder#bindObject

private <T> Object bindObject(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,
	Context context, boolean allowRecursiveBinding) {
	ConfigurationProperty property = findProperty(name, context);
	if (property == null && containsNoDescendantOf(context.getSources(), name)) {
		return null;
	}
	AggregateBinder<?> aggregateBinder = getAggregateBinder(target, context);
	if (aggregateBinder != null) {
		return bindAggregate(name, target, handler, context, aggregateBinder);
	}
	if (property != null) {
		try {
			return bindProperty(target, context, property);
		}
		catch (ConverterNotFoundException ex) {
			// We might still be able to bind it as a bean
			Object bean = bindBean(name, target, handler, context, allowRecursiveBinding);
			if (bean != null) {
				return bean;
			}
			throw ex;
		}
	}
	return bindBean(name, target, handler, context, allowRecursiveBinding);
}

3. 转换的执行

最终的转换逻辑是根据源类型和目标类型查找与之匹配的转换器(Converter)并进行转换,如果找不到转换器会抛出异常。

org.springframework.core.convert.support.GenericConversionService#convert(java.lang.Object, org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor)

@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() + "]");
	}
	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);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值