前言
前面聊了HttpMessageConverter,它的名称叫消息转换器,所以它面向的是消息体,和Http强相关,所以该接口所在的包为:org.springframework.http.converter
数据转换,顾名思义就是数据类型之间的转换,但是对于数据转换,有的是可以进行转化的,例如字符串转整型,但是有些数据类型之间是不能进行转换的,例如从“aaa”字符串到整型的转换。
不同的框架,肯定都有自己的数据转换的实现,比如MyBatis、Hibernate等这些转换器都是必备的。然后作为这么强大的Spring,它肯定也缺席不了。org.springframework.core.convert.converter.Converter它位于核心包中,所以它不仅仅运用于Spring MVC等web环境,比如spring-jdbc等都是有使用到的~
数据转换在框架设计中是非常重要的一环,它能让你的框架更普适,更通用,更自动化,解决的问题更多,所以我个人认为,了解Spring数据转换的设计思想,以及它的常用实现是非常有必要的。
若是源生Servlet开发,你能想象到那种低下的开发效率吗以及漫天遍地的“垃圾代码”吗?
关于Spring中的数据转换,首先需要了解两大主要分支:
- Converter<S, T>:是Spring中最为简单的一个接口。位于包:org.springframework.core.convert.converter。 相关的顶层接口(类)有:ConditionalConverter、GenericConverter、ConverterFactory、ConvertingComparator、ConverterRegistry
- ConversionService:用于类型转换的服务接口。这是进入转换系统的入口点。位于包:org.springframework.core.convert。相关的顶层接口(类)有:ConversionService、FormattingConversionService、DefaultConversionService、ConversionServiceFactoryBean、FormattingConversionServiceFactoryBean…
注意各子接口,实现类不一定都是core包里,可能在context包、web包等等~。他俩体系都是@since 3.0
Converter<S, T>
Spring的Converter是可以将一种类型转换成另一种类型的一个对象,它的接口定义非常的的简单。
// 实现此接口的 大都会实现ConditionalConverter // 请保持线程安全~~ @FunctionalInterface public interface Converter<S, T> { // 把S转成T @Nullable T convert(S source); }
Spring提供了3种converter接口,分别是Converter、ConverterFactory和GenericConverter.一般用于1:1, 1:N, N:N的source->target类型转化。
Converter接口 :使用最简单,最不灵活;
ConverterFactory接口 :使用较复杂,比较灵活;
GenericConverter接口 :使用最复杂,也最灵活;
Converter
Converter的实现类举例:该接口Spring内部的实现也非常多,大多数都是以内部类的形式实现(因为它是一个@FunctionalInterface嘛)
// ObjectToStringConverter final class ObjectToStringConverter implements Converter<Object, String> { @Override public String convert(Object source) { return source.toString(); } }
// StringToCharsetConverter @since 4.2 @Override public Charset convert(String source) { return Charset.forName(source); } // StringToPropertiesConverter @Override public Properties convert(String source) { try { Properties props = new Properties(); // Must use the ISO-8859-1 encoding because Properties.load(stream) expects it. props.load(new ByteArrayInputStream(source.getBytes(StandardCharsets.ISO_8859_1))); return props; }catch (Exception ex) { // Should never happen. throw new IllegalArgumentException("Failed to parse [" + source + "] into Properties", ex); } } // StringToTimeZoneConverter @since 4.2 @Override public TimeZone convert(String source) { return StringUtils.parseTimeZoneString(source); } //ZoneIdToTimeZoneConverter @since 4.0 @Override public TimeZone convert(ZoneId source) { return TimeZone.getTimeZone(source); } // StringToBooleanConverter 这个转换器很有意思 哪些代表true,哪些代表fasle算是业界的一个规范了 // 这就是为什么,我们给传值1也会被当作true来封装进Boolean类型的根本原因所在~ static { trueValues.add("true"); trueValues.add("on"); trueValues.add("yes"); trueValues.add("1"); falseValues.add("false"); falseValues.add("off"); falseValues.add("no"); falseValues.add("0"); } // StringToUUIDConverter @since 3.2 @Override public UUID convert(String source) { return (StringUtils.hasLength(source) ? UUID.fromString(source.trim()) : null); } // StringToLocaleConverter @Override @Nullable public Locale convert(String source) { return StringUtils.parseLocale(source); } // SerializingConverter:把任意一个对象,转换成byte[]数组,唯独这一个是public的,其它的都是Spring内置的 public class SerializingConverter implements Converter<Object, byte[]> { // 序列化器:DefaultSerializer 就是new ObjectOutputStream(outputStream).writeObject(object) // 就是简单的把对象写到输出流里~~ private final Serializer<Object> serializer; public SerializingConverter() { this.serializer = new DefaultSerializer(); } public SerializingConverter(Serializer<Object> serializer) { // 自己亦可指定实现。 Assert.notNull(serializer, "Serializer must not be null"); this.serializer = serializer; } @Override public byte[] convert(Object source) { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024); try { this.serializer.serialize(source, byteStream); // 把此输出流转为byte[]数组~~~~~~ return byteStream.toByteArray(); } catch (Throwable ex) { throw new SerializationFailedException("Failed to serialize object using " + this.serializer.getClass().getSimpleName(), ex); } } }
Converter
接口非常的简单,所以除了SerializingConverter
一个是外部类,我们可以拿来使用外,其余的都是Spring内部自己使用的。从此可以看出:此接口一般也用于我们自己去实现,即:自定义数据转换器。
自定义转换器的一个Demo:
// 把形如这样的字符串: "fsx:18" 转换为Person对象 public class PersonConverter implements Converter<String, Person> { @Override public Person convert(String source) { if (StringUtils.isEmpty(source)) { return null; } String[] strings = StringUtils.delimitedListToStringArray(source, ":"); Person person = new Person(); person.setName(strings[0]); person.setAge(Integer.valueOf(strings[1])); return person; } public static void main(String[] args) { PersonConverter personConverter = new PersonConverter(); System.out.println(personConverter.convert("fsx:18")); //Person{name='fsx', age=18} } }
Converter
接口非常的简单,所以除了SerializingConverter
一个是外部类,我们可以拿来使用外,其余的都是Spring内部自己使用的。从此可以看出:此接口一般也用于我们自己去实现,即:自定义数据转换器。
自定义转换器的一个Demo:
// 把形如这样的字符串: "fsx:18" 转换为Person对象 public class PersonConverter implements Converter<String, Person> { @Override public Person convert(String source) { if (StringUtils.isEmpty(source)) { return null; } String[] strings = StringUtils.delimitedListToStringArray(source, ":"); Person person = new Person(); person.setName(strings[0]); person.setAge(Integer.valueOf(strings[1])); return person; } public static void main(String[] args) { PersonConverter personConverter = new PersonConverter(); System.out.println(personConverter.convert("fsx:18")); //Person{name='fsx', age=18} } }
备注:在Spring内部消息转换器的注册、使用一般都结合ConversionService
这个接口
ConditionalConverter
根据source和target来做条件判断,从而可以判断哪个转换器生效,哪个不生效之类的。
// @since 3.2 出现稍微较晚 public interface ConditionalConverter { boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); }
org.springframework.core.convert.TypeDescriptor也是一个Spring的基础类(类似ResolvableType)这种,若有需要我们平时也可以使用它。 它能够把基础类型、MethodParameter、Field、org.springframework.core.convert.Property、Class等都描述进来。并且提供如下非常方便方法:
// @since 3.0 public class TypeDescriptor implements Serializable { public Class<?> getType() { return this.type; } public ResolvableType getResolvableType() { return this.resolvableType; } public Object getSource() { return this.resolvableType.getSource(); } public String getName(); public boolean isPrimitive(); public Annotation[] getAnnotations(); public boolean hasAnnotation(Class<? extends Annotation> annotationType); public <T extends Annotation> T getAnnotation(Class<T> annotationType); public boolean isAssignableTo(TypeDescriptor typeDescriptor); public boolean isCollection(); public boolean isArray(); public boolean isMap(); public TypeDescriptor getMapKeyTypeDescriptor(); public TypeDescriptor getMapValueTypeDescriptor() // 静态方法:可吧基础类型、任意一个class类型转为这个描述类型 依赖于下面的valueOf方法 source为null 返回null public static TypeDescriptor forObject(@Nullable Object source); public static TypeDescriptor valueOf(@Nullable Class<?> type); // 把集合转为描述类型~ public static TypeDescriptor collection(Class<?> collectionType, @Nullable TypeDescriptor elementTypeDescriptor) public static TypeDescriptor map(Class<?> mapType, @Nullable TypeDescriptor keyTypeDescriptor, @Nullable TypeDescriptor valueTypeDescriptor); public static TypeDescriptor array(@Nullable TypeDescriptor elementTypeDescriptor); public static TypeDescriptor nested(MethodParameter methodParameter, int nestingLevel); public static TypeDescriptor nested(Field field, int nestingLevel); public static TypeDescriptor nested(Property property, int nestingLevel); }
ConditionalConverter的继承树:
ConditionalGenericConverter这个子接口,就是把GenericConverter和ConditionalConverter联合起来了。而GenericConverter我们上面提到了,它一般用于处理N:N的转换,因此它的子类们放在下面讲会更合适~
NumberToNumberConverterFactory:它是个ConverterFactory,所以也放下面
AbstractConditionalEnumConverter:枚举类型的转换
// @since 4.3 也是只能Spring内部自己用的 abstract class AbstractConditionalEnumConverter implements ConditionalConverter { // 它借助了ConversionService这个接口 需要外部自定义转换逻辑~~ private final ConversionService conversionService; protected AbstractConditionalEnumConverter(ConversionService conversionService) { this.conversionService = conversionService; } @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { // 拿到source所有实现的接口 若没有实现任何接口,永远返回true for (Class<?> interfaceType : ClassUtils.getAllInterfacesForClassAsSet(sourceType.getType())) { // 最终是委托给conversionService去做这件事了~~~~ if (this.conversionService.canConvert(TypeDescriptor.valueOf(interfaceType), targetType)) { return false; } } return true; } }
它的两个子类实现:EnumToIntegerConverter和EnumToStringConverter就是调用了source.ordinal()和source.name()。若你想要实现自己的枚举自定义属性的转换,其实是可以继承AbstractConditionalEnumConverter它的,但是Spring并没有公开它,so~~~你还是自己写吧
ConverterFactory
ConverterFactory:range范围转换器的工厂:可以将对象从S转换为R的子类型(1:N)
public interface ConverterFactory<S, R> { //Get the converter to convert from S to target type T, where T is also an instance of R <T extends R> Converter<S, T> getConverter(Class<T> targetType); }
它的实现类不多:
final class IntegerToEnumConverterFactory implements ConverterFactory<Integer, Enum> { // ConversionUtils.getEnumType表示拿出枚举的class类型 @Override public <T extends Enum> Converter<Integer, T> getConverter(Class<T> targetType) { return new IntegerToEnum(ConversionUtils.getEnumType(targetType)); } // 内部类的实现 把Integer转为Enum的子类型~~~ 相当于根据integer找到一个enum(注意此处根据角标来找的) private class IntegerToEnum<T extends Enum> implements Converter<Integer, T> { private final Class<T> enumType; public IntegerToEnum(Class<T> enumType) { this.enumType = enumType; } @Override public T convert(Integer source) { return this.enumType.getEnumConstants()[source]; } } } // StringToEnumConverterFactory 大体同上 return (T) Enum.valueOf(this.enumType, source.trim()) ...
该工厂就是用来创建一个converter
,把目标类型转换成子类型,所以它是1->N的。注意:Spring内置的实现也都是外部不可访问的