Spring ConversionService 类型转换(一)Converter

Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)

Spring ConversionService 类型转换系列文章:

  1. Spring ConversionService 类型转换(一)Converter
  2. Spring ConversionService 类型转换(二)ConversionService

JDK 提供的 PropertyEditor 只能将 String 类型转为 Object,如果要将一种 Object 类型转换成另一种 Object 类型就只能使用 Spring 提供的 ConversionService 了,这些类都位于 org.springframework.core.convert 包下。

一、ConversionService 使用

private ConversionService conversionService;
@Before
public void before() {
conversionService = new DefaultConversionService();
} @Test
public void test() {
ConversionService conversionService = new DefaultConversionService();
Integer value = conversionService.convert("1", Integer.class);
Assert.assertTrue(value == 1);
}

在 DefaultConversionService 组件中已经注册了 Spring 默认的觉转换器,可以分为以下几类:

  • Converter 一对一转换,把 S 类型转化成 T 类型,最常用的转换器
  • ConverterFactory 一对 N 转换
  • GenericConverter N 对 N 转换

二、三种转换器

2.1 Converter(1:1)

(1) 接口

@FunctionalInterface
public interface Converter<S, T> {
T convert(S source);
}

Converter接口很简单,就是把 S 类型转化成 T 类型。我们看一下使用方法:

(2) 测试

@Test
public void converterTest() {
// ObjectToStringConverter
Assert.assertEquals("false", conversionService.convert(false, String.class)); // StringToBooleanConverter
Assert.assertTrue(conversionService.convert("true", Boolean.class));
}

(3) ObjectToStringConverter 分析

ObjectToStringConverter 和 StringToBooleanConverter 都是在 DefaultConversionService 中内置的。

final class ObjectToStringConverter implements Converter<Object, String> {
@Override
public String convert(Object source) {
return source.toString();
}
}

2.2 ConverterFactory(1:N)

(1) 接口

public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

R 的子类都可以统一由这个 ConverterFactory 进行转换。

(2) 测试

// 测试 ConverterFactory StringToNumberConverterFactory
@Test
public void converterFactoryTest() {
Assert.assertTrue(conversionService.convert("1.2", double.class) == 1.2d);
Assert.assertTrue(conversionService.convert("1", int.class) == 1);
Assert.assertTrue(conversionService.convert("0x10", byte.class) == 0x10);
}

这里用到了 StringToNumberConverterFactory 把 String 转化成了 Number 的各个子类型,代码其实很简单:

(3) StringToNumberConverterFactory 分析

final class StringToNumberConverterFactory implements ConverterFactory<String, Number> {
@Override
public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToNumber<>(targetType);
} private static final class StringToNumber<T extends Number> implements Converter<String, T> {
private final Class<T> targetType;
public StringToNumber(Class<T> targetType) {
this.targetType = targetType;
} @Override
public T convert(String source) {
if (source.isEmpty()) {
return null;
}
// String 类型转换成 Number
return NumberUtils.parseNumber(source, this.targetType);
}
}
}

2.3 GenericConverter(N:N)

(1) 接口

public interface GenericConverter {
// 可以转换的类型
Set<ConvertiblePair> getConvertibleTypes(); Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType); final class ConvertiblePair {
private final Class<?> sourceType;
private final Class<?> targetType;
}
} // 匹配 GenericConverter
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
} public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}

GenericConverter 是 N:N 的转化,支持转化的所有类型都写在了属性 Set 内。

(2) 测试

// 测试 GenericConverter CollectionToCollectionConverter
@Test
public void genericConverterTest() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Set<String> set = (Set<String>) conversionService.convert(list,
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Integer.class)),
TypeDescriptor.collection(Set.class, TypeDescriptor.valueOf(String.class)));
// List<Integer> -> Set<String>
Assert.assertEquals("1", set.toArray(new String[0])[0]);
}

这里用到了 CollectionToCollectionConverter

(3) 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
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Collection<?> sourceCollection = (Collection<?>) source; // 集合类型
boolean copyRequired = !targetType.getType().isInstance(source);
// 1. targetType 集合类型没变,不用转换
if (!copyRequired && sourceCollection.isEmpty()) {
return source;
} // 集合元素类型
TypeDescriptor elementDesc = targetType.getElementTypeDescriptor();
// 2. targetType 集合元素没有指定类型,即 Object,且集合类型没变
if (elementDesc == null && !copyRequired) {
return source;
} // 创建一个空集合
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(),
(elementDesc != null ? elementDesc.getType() : null), sourceCollection.size()); // 3. targetType 集合元素没有指定类型,则元素不用转换类型
if (elementDesc == null) {
target.addAll(sourceCollection);
// 4. conversionService 将 sourceElement 转换为 targetElement 类型
} else {
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);
}
}

参考:

  1. 《Spring 学习记录 3 ConversionService》:https://www.cnblogs.com/abcwt112/p/7447435.html

每天用心记录一点点。内容也许不重要,但习惯很重要!

Spring ConversionService 类型转换(一)Converter的更多相关文章

  1. Spring ConversionService 类型转换(二) ConversionService

    Spring ConversionService 类型转换(二) ConversionService Spring 系列目录(https://www.cnblogs.com/binarylei/p/1 ...

  2. Spring类型转换(Converter)

    Spring的类型转换 以前在面试中就有被问到关于spring数据绑定方面的问题,当时对它一直只是朦朦胧胧的概念,最近稍微闲下来有时间看了一下其中数据转换相关的内容,把相应的内容做个记录. 下面先说明 ...

  3. spring 之 类型转换

    在spring中, 提供了至少三种的 类型转换方式:   ConversionServiceFactoryBean, FormattingConversionServiceFactoryBean, C ...

  4. spring自动类型转换========Converter和PropertyEditor

    Spring有两种自动类型转换器,一种是Converter,一种是propertyEditor. 两者的区别:Converter是类型转换成类型,Editor:从string类型转换为其他类型. 从某 ...

  5. Spring MVC 类型转换

    SpringMVC类型转换: 1 日期类型转换: private Date birthday; <label for="">生日:<input type=&quo ...

  6. 2. Spring早期类型转换,基于PropertyEditor实现

    青年时种下什么,老年时就收获什么.关注公众号[BAT的乌托邦],有Spring技术栈.MyBatis.JVM.中间件等小而美的原创专栏供以免费学习.分享.成长,拒绝浅尝辄止.本文已被 https:// ...

  7. Spring MVC类型转换

    类型转换器引入 为什么页面上输入"12",可以赋值给Handler方法对应的参数?这是因为框架内部帮我们做了类型转换的工作.将String转换成int 但默认类型转换器并不是可以将 ...

  8. spring 之 类型转换 2

    spring内置的转换器 在spring xml 文件中,配置属性的时候, 不管实际是 list 还是map ,还是Date, 或者原生的java 类型, 我们只能配置xml 给它们. 那么 spri ...

  9. 解决Spring MVC报No converter found for return value of type:class java.util.ArrayList问题

    一.背景 在搭建一套Spring+SpringMVC+Mybatis(SSM)的环境(搭建步骤会在以后博客中给出),结果运行 程序时,适用@ResponseBody注解进行返回List<对象&g ...

随机推荐

  1. cakephp2.7的学习笔记1 —— 安装与配置

    CakePHP2.7的安装 下载 https://github.com/cakephp/cakephp/releases 解压后扔进你的www目录就可以直接访问 按照提示,修改这两项配置,替换成你喜欢 ...

  2. sqlite3调试

    [sqlite3调试] 1.adb shell 激活模拟器shell. 2.cd /data/data/com.xxx.xxx/databases 进入app 数据库目录. 3.ls 查看有哪些数据库 ...

  3. pip安装离线包

    离线包从pypi.org下载 pip download  -r requirements.txt  -d  /tmp/paks/ 在linux下       1.下载指定的包到指定文件夹.       ...

  4. ezmorph将一种对象转换成另外一种对象

    EZMorph支持原始数据类型(Primitive),对象(Object),多维数组转换与DynaBeans的转换.兼容JDK1.3.1,整个类库大小只有76K左右. 在Java EE开发常用的str ...

  5. artTemplate/template.js模板将时间格式化为正常的日期

    网上提供的方法用不了 自己琢磨的 注意:ajax的异步方式必须设置成true才行,false不起作用.async: true: <script> function timestamp (v ...

  6. python--第二天总结

    一.作用域只要变量在内存中存在,则就可以使用.(栈) 二.三元运算result = 值result = 值1 if 条件 else 值2 如果条件为真:result = 值1如果条件为假:result ...

  7. 善于利用python中的os模块

    作为一个程序猿,平时善于利用python中的os模块进行路径等操作,会省去很多麻烦,下面总结一下我平时经常用到的方法: import os os.getcwd() # 获取当前文件所在的目录 os.p ...

  8. 【git】gitignore

    gitignore git专门有个文件用来管理那些不被纳入版本库的文件,这个文件是 [.gitignore],所有不被包含的都能放进去,但这个是有前提的. 前提 前提是文件如果没被git客户端trac ...

  9. TOJ 2778 数据结构练习题――分油问题(广搜和哈希)

    描述 设有大小不等的三个无刻度的油桶,分别能盛满x,y,z公升油.初始时,第一个油桶盛满油,第二.三个油桶为空,在某一个油桶上分出targ公升油. 输入 输入包含多组测试数据.每组数据包含一行.分别x ...

  10. day 03

    1.数字类型 int 数字主要是用于计算用的,使用方法并不是很多,就记住一种就可以: bit_length()  当前十进制用二进制表示时,最少使用的位数 s = 5 print(s.bit_leng ...