SpringBoot转换服务ApplicationConversionService

1. 源码分析

Intellij idea Diagrams

 具体分析如下:

2.GenericConversionService 实现方式

最左侧通用转换服务(GenericConversionService)使用案例:

//  以下为测试类

GenericConversionService service = new GenericConversionService();

// 将 String 类型的时间转换为 util.Date
service.addConverter(new Converter<String, Date>() {
    @Override
    public Date convert(String source) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            return format.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
});
// 将 Integer 转换为 String,Double,Boolean
service.addConverter(new GenericConverter() {
    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return new HashSet<ConvertiblePair>() {{
            add(new ConvertiblePair(Integer.class, String.class));
            add(new ConvertiblePair(Integer.class, Double.class));
            add(new ConvertiblePair(Integer.class, Boolean.class));
        }};
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        Class<?> type = targetType.getType();
        if (String.class.equals(type)) {
            return source.toString();
        } else if (Double.class.equals(type)) {
            return Double.valueOf(source.toString());
        } else if (Boolean.class.equals(type)) {
            return Boolean.valueOf(source.toString());
        }
        return null;
    }
});
// String 转 Boolean
service.addConverter(String.class, Boolean.class, new Converter<String, Boolean>() {
    @Override
    public Boolean convert(String source) {
        return Boolean.parseBoolean(source);
    }
});
// String 转 Integer,Long
service.addConverterFactory(new ConverterFactory<String, Number>() {
    @Override
    public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
        if(targetType.getTypeName().equals(Integer.class.getTypeName())) {
            return new Converter<String, T>() {
                @Override
                public T convert(String source) {
                    return (T) NumberUtils.parseNumber(source, Integer.class);
                }
            };
        } else if(targetType.getTypeName().equals(Long.class.getTypeName())) {
            return new Converter<String, T>() {
                @Override
                public T convert(String source) {
                    return (T) NumberUtils.parseNumber(source, Long.class);
                }
            };
        }
        return null;
    }
});

Boolean checkTimeToDate = service.canConvert(Map.class, List.class);
System.out.println("Map能否转换为List:" + checkTimeToDate);
Date targetDate = service.convert("2021-12-20 13:01:01", Date.class);
System.out.println(targetDate);
String targetString = service.convert(1, String.class);
System.out.println(targetString);
Double targetDouble = service.convert(1, Double.class);
System.out.println(targetDouble);
Boolean targetBoolean = service.convert(1, Boolean.class);
System.out.println(targetBoolean);
Boolean stringToBoolean = service.convert("true", Boolean.class);
System.out.println(stringToBoolean);
Integer stringToInteger = service.convert("200", Integer.class);
System.out.println(stringToInteger);
Long stringToLong = service.convert("300", Long.class);
System.out.println(stringToLong);

使用一个 ConcurrentReferenceHashMap Spring封装的线程安全HashMap,作为缓存,可能会被垃圾回收,只做临时存储,默认Map长度 64。

使用 Converters 存储实际转换器。

GenericConversionService.java 第78行

private final Converters converters = new Converters();

private final Map<ConverterCacheKey, GenericConverter> converterCache = new ConcurrentReferenceHashMap<>(64);

必知:GenericConverter 通用转换器,定义了 可转换类型 和 转换方法。

Converters具体存储数据格式如下:

GenericConversionService.java 第503行两个集合

Set: [
    GenericConverter: {
        ConvertiblePair: {
            sourceType: '原类型',
            targetType: '目标类型',
            equals: '重写equals方法'
        },
        Converter<Object, Object> : '转换接口'
    }
]

Map {
    ConvertiblePair: ConvertersForPair: {
        converters: [ GenericConverter, GenericConverter, GenericConverter ]
    }
}

上图 Set 集合似乎没啥用,从没用到过,不做解释。

核心使用 线程安全HashMap ConcurrentHashMap 去做数据存储和查询。

通过 ConvertiblePair 重写HasCode,调用 Map.get 得到 ConvertersForPair。

ConvertersForPair.converters 是一个 ConcurrentLinkedDeque 线程安全队列,每次向前插数据。

循环从索引0开始++,所以后加入的则会被先匹配。

3. ApplicationConversionService 添加转换器

ApplicationConversionService 负责定义转换器,其他实现类在其上加入特有转换器。

ApplicationConversionService.java 第96行

public static void configure(FormatterRegistry registry) {
	DefaultConversionService.addDefaultConverters(registry);
	DefaultFormattingConversionService.addDefaultFormatters(registry);
	addApplicationFormatters(registry);
	addApplicationConverters(registry);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值