GenericConversionService学习

简介

在 Spring Framework 中,GenericConversionService 是 ConversionService 接口的一个实现,它提供了更高级和灵活的类型转换功能。与 DefaultConversionService 相比,GenericConversionService 支持更复杂的类型转换逻辑,特别是涉及泛型的情况。

GenericConversionService 允许你注册多种类型的转换器,包括:

Converter:用于将一个类型的对象转换为另一个类型的对象。
GenericConverter:提供了更高级别的转换,允许源类型和目标类型都是泛型。
ConditionalConverter:与 Converter 类似,但它提供了一个额外的条件来判断转换是否适用。
通过 GenericConversionService,你可以实现自定义的转换逻辑,处理复杂的类型转换场景。例如,你可以注册一个 GenericConverter 来处理两个都是泛型类型的转换,这在处理集合、Map 或其他复杂类型时非常有用。

源码

//具体添加转换器实现逻辑
public class GenericConversionService implements ConfigurableConversionService {
    //如果当前对象和目标对象直接不需要进行类型转换,那么缓存记录时,会把当前对象和NO_OP_CONVERTER做映射
	private static final GenericConverter NO_OP_CONVERTER = new NoOpConverter("NO_OP");

    //如果当前对象和目标对象的转换,无法通过现有的转换器完成,
    //那么缓存中记录时,会把当前对象和NO_MATCH做映射
	private static final GenericConverter NO_MATCH = new NoOpConverter("NO_MATCH");

    //Converters是GenericConversionService的内部类,用于管理(添加、删除、查找)转换器们。
    //也就说对ConverterRegistry接口的实现最终是委托给它去完成的,它是整个转换服务正常work的内核
	private final Converters converters = new Converters();
    
    //它用两个成员变量来管理转换器们,其中converterCache是缓存用于加速查找,
    //因此更为重要的便是Converters喽。 
	private final Map<ConverterCacheKey, GenericConverter> converterCache 
        = new ConcurrentReferenceHashMap<>(64);
    
    //-------------------------ConverterRegistry注册相关接口的具体实现------------------------
    /**
    对于三种转换器Converter、ConverterFactory、GenericConverter在添加到Converters之前都统一被适配为了GenericConverter,这样做的目的是方便统一管理。

对应的两个适配器是ConverterAdapter和ConverterFactoryAdapter,它俩都是ConditionalGenericConverter的内部类。
    */
   
    //增加一个1:1的转化器
	@Override
	public void addConverter(Converter<?, ?> converter) {
	//getRequiredTypeInfo是一个工具方法,功能是返回传入converter的具体的泛型参数数组
	//泛型参数都被ResolvableType进行包裹,长度为2,一个是原对象类型,一个是目标对象类型
		ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);
		//对代理进行判断--如果当前添加的converter是被spring代理过的对象,需要拿到真正的converter对象类型
		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都通过ConverterAdapter转换为GenericConverter
		addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));
	}

//和上面那个重载方法相比,少了去解析出convert泛型参数的过程
	@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)));
	}

//上面两个重载方法最终调用的方法--这里参数需要的是GenericConverter
//因为我们之前说过有1:1,1:n,n:n三种转换器,因此我们这边把所有转换器都转换为通用的GenericConverter(n:n)进行管理
	@Override
	public void addConverter(GenericConverter converter) {
	//converters是一个内部类,它管理所有转换器,包括添加、删除、查找。
	//这个一会在细聊
		this.converters.add(converter);
		//清除缓存集合
		invalidateCache();
	}

//添加一个1:n的转换器
	@Override
	public void addConverterFactory(ConverterFactory<?, ?> factory) {
	   //同样因为转换器的泛型参数不确定,因此需要先解析出来
		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?");
		}
		//ConverterFactoryAdapter是将ConverterFactory转换为通用的GenericAdapter
		addConverter(new ConverterFactoryAdapter(factory,
				new ConvertiblePair(typeInfo[0].toClass(), typeInfo[1].toClass())));
	}
    
    //前两个方法都会调用到第三个方法上,每调用一次addConverter()方法都会清空缓存,
    //也就是converterCache.clear()。所以动态添加转换器对性能是有损的,因此使用时候需稍加注意一些。

//移除某个转换器
	@Override
	public void removeConvertible(Class<?> sourceType, Class<?> targetType) {
	//可以看出是converters管理某个转换器的删除
		this.converters.remove(sourceType, targetType);
		//清空缓存
		invalidateCache();
	}
    //-------------------------ConversionService注册相关接口的具体实现------------------------

	@Override
	public boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType) {
		Assert.notNull(targetType, "Target type to convert to cannot be null");
		//将Class包装为TypeDescriptor,然后交给重载方法进行处理
		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");
		if (sourceType == null) {
			return true;
		}
		//能否进行转换就是去找到有无可用的转换器
		//这里如果一开始缓存中没有,那么找到后会加入缓存中,这样一会进行转换的时候,就会直接从缓存中取
		//如果找不到也会放入缓存集合--相当于做个标记
		GenericConverter converter = getConverter(sourceType, targetType);
		return (converter != null);
	}

	/**
      是否不需要进行任何类型转换
	 */
	public boolean canBypassConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
		Assert.notNull(targetType, "Target type to convert to cannot be null");
		if (sourceType == null) {
			return true;
		}
		//如果不需要进行任何类型转换,那么getConverter会返回NO_OP_CONVERTER---表示当前类型对不需要进行转换
		GenericConverter converter = getConverter(sourceType, targetType);
		return (converter == NO_OP_CONVERTER);
	}
    //可以看到能否进行转换的核心在于能不能找到一个转换器来进行转换,
    //所以我们的目光还是要放到getConverter上面去
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
		//构建缓存key
		ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
		//先尝试从缓存中去获取
		GenericConverter converter = this.converterCache.get(key);
		if (converter != null) {
			//缓存中存在
			//这里提前说一下:如果某个类型对不存在与之关联的转换器,那么在第一次查找无果后
			//会在缓存中进行标记,即当前key--->NO_MATCH
			//因此如果这里存在转换器,但是为NO_MATCH ,表示不存在对应的转换器可以转换该类型对,返回null即可
			return (converter != NO_MATCH ? converter : null);
		}
        //converters负责通过类型对去查找到指定的转换器
		converter = this.converters.find(sourceType, targetType);
		//如果找不到
		if (converter == null) {
		//如果source和target之间是父子关系,那么返回NO_OP_CONVERTER,表示不需要进行类型转换
		//否则返回null
			converter = getDefaultConverter(sourceType, targetType);
		}
        //如果到这里找到了对应的转换器,那么会放入缓存中
		if (converter != null) {
			this.converterCache.put(key, converter);
			return converter;
		}
        //走到这里,说明找不到,那么当前类型对会和一个NO_MATCH的转换器进行关联
		this.converterCache.put(key, NO_MATCH);
		return null;
	}
	
	//如果source和target之间是父子关系,那么返回NO_OP_CONVERTER,表示不需要进行类型转换
	//否则返回null
	@Nullable
	protected GenericConverter getDefaultConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
		return (sourceType.isAssignableTo(targetType) ? NO_OP_CONVERTER : null);
	}
	//上面的getConverter是依靠Converters.find()方法完成
    //... 其他内容请参考上面链接
    //内部类 Converters
}

示例

import org.springframework.core.convert.ConversionService;  
import org.springframework.core.convert.support.GenericConversionService;  
import org.springframework.core.convert.converter.Converter;  
  
public class GenericConversionServiceExample {  
  
    public static void main(String[] args) {  
        // 创建一个 GenericConversionService 实例  
        ConversionService conversionService = new GenericConversionService();  
  
        // 注册自定义转换器  
        conversionService.addConverter(new StringToUpperCaseConverter());  
  
        // 执行类型转换  
        String sourceValue = "hello";  
        String targetValue = conversionService.convert(sourceValue, String.class);  
  
        System.out.println(targetValue); // 输出:HELLO  
    }  
  
    // 自定义转换器,将字符串转换为大写  
    static class StringToUpperCaseConverter implements Converter<String, String> {  
        @Override  
        public String convert(String source) {  
            return source.toUpperCase();  
        }  
    }  
}

在这个例子中,我们创建了一个 GenericConversionService 实例,并注册了一个自定义的 Converter,它将字符串转换为大写。然后,我们使用 ConversionService 的 convert 方法将字符串 “hello” 转换为大写形式。

GenericConversionService 还支持条件转换器(ConditionalConverter),这允许你定义转换器适用的条件。这对于根据特定条件选择不同转换逻辑的场景非常有用。

此外,Spring 还提供了许多内置的类型转换器,这些转换器在 GenericConversionService 中默认是启用的。这意味着你可以直接使用诸如字符串到数字、字符串到枚举等常见转换,而无需注册自定义转换器。

总之,GenericConversionService 是 Spring 中用于执行复杂类型转换的强大工具,它提供了比 DefaultConversionService 更高级别的转换功能,并支持自定义转换器注册。

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Spring Boot 中,我们可以通过实现 Converter 接口来自定义类型转换器,但是有时候我们发现自定义的转换器并没有生效。这可能是因为 Spring Boot 默认使用的是 GenericConversionService,而不是我们自定义的 ConversionService。 解决这个问题的方法有两种: 1. 在自定义转换器上添加 @Component 注解,将其注册到 Spring 容器中,并在需要使用该转换器的地方使用 @Autowired 注入。这样就可以确保我们自定义的转换器会被使用。 2. 自定义一个 ConversionService,将其注册到 Spring 容器中,并在需要使用该转换器的地方使用 @Qualifier 注解指定使用我们自定义的转换器。这种方法需要手动配置 ConversionService,但是可以更灵活地控制转换器的使用。 下面是第二种方法的示例代码: ```java @Configuration public class ConversionConfig { @Bean public ConversionService conversionService() { DefaultConversionService conversionService = new DefaultConversionService(); // 注册自定义转换器 conversionService.addConverter(new MyConverter()); return conversionService; } } ``` 在需要使用自定义转换器的地方,可以使用 @Qualifier 注解指定使用我们自定义的 ConversionService,例如: ```java @RestController public class DemoController { @Autowired @Qualifier("conversionService") private ConversionService conversionService; @GetMapping("/test") public String test(@RequestParam("myParam") MyParam myParam) { // 使用自定义转换器将字符串转换为自定义类型 MyParam // ... return "success"; } } ``` 希望能帮助到你!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值