我是靠谱客的博主 单身硬币,这篇文章主要介绍Spring Converter 体系,现在分享给大家,希望可以做个参考。

最近封装 RPC 相关的模块,领导说数据转换可以考虑使用 Spring 原有的 Converter 体系。

  • Converter<S, T> 最简单的转换器、相关的顶层接口有 ConditionalConverterGenericConverterConverterFactoryConvertingComparatorConverterRegistry
  • ConversionService Spring 数据转换的入口、它根据相关参数将调用路由到具体的 Converter。 相关的接口和类 FormattingConversionServiceDefaultConversionServiceConversionServiceFactoryBeanFormattingConversionServiceFactoryBean

Converter

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/** * A converter converts a source object of type {@code S} to a target of type {@code T}. * * <p>Implementations of this interface are thread-safe and can be shared. * * <p>Implementations may additionally implement {@link ConditionalConverter}. * * @author Keith Donald * @since 3.0 * @param <S> the source type * @param <T> the target type */ @FunctionalInterface public interface Converter<S, T> { /** * Convert the source object of type {@code S} to target type {@code T}. * @param source the source object to convert, which must be an instance of {@code S} (never {@code null}) * @return the converted object, which must be an instance of {@code T} (potentially {@code null}) * @throws IllegalArgumentException if the source cannot be converted to the desired target type */ @Nullable T convert(S source); }

实现该接口要确保其线程安全、一般来说除了实现该接口、还会实现 ConditionalConverter 接口

看一些常见的 Spring 内部提供给我们使用的

字符串转布尔

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
final class StringToBooleanConverter implements Converter<String, Boolean> { private static final Set<String> trueValues = new HashSet<>(8); private static final Set<String> falseValues = new HashSet<>(8); 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"); } @Override @Nullable public Boolean convert(String source) { String value = source.trim(); if (value.isEmpty()) { return null; } value = value.toLowerCase(); if (trueValues.contains(value)) { return Boolean.TRUE; } else if (falseValues.contains(value)) { return Boolean.FALSE; } else { throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); } } }

字符转 Charset

复制代码
1
2
3
4
5
6
7
8
9
class StringToCharsetConverter implements Converter<String, Charset> { @Override public Charset convert(String source) { return Charset.forName(source); } }

Converter 是一比一之间的转换、相对来说是比较简单的

ConverterFactory

这个是一比多之间的转换

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
public interface ConverterFactory<S, R> { /** * Get the converter to convert from S to target type T, where T is also an instance of R. * @param <T> the target type * @param targetType the target type to convert to * @return a converter from S to T */ <T extends R> Converter<S, T> getConverter(Class<T> targetType); }

看下 String 转为各种枚举类

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> { @Override public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) { return new StringToEnum(ConversionUtils.getEnumType(targetType)); } private static class StringToEnum<T extends Enum> implements Converter<String, T> { private final Class<T> enumType; public StringToEnum(Class<T> enumType) { this.enumType = enumType; } @Override @Nullable public T convert(String source) { if (source.isEmpty()) { // It's an empty enum identifier: reset the enum value to null. return null; } return (T) Enum.valueOf(this.enumType, source.trim()); } } }

GenericConverter

N:N 的转换

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface GenericConverter { // 这里就返回了转换关系、是一个集合 @Nullable Set<ConvertiblePair> getConvertibleTypes(); @Nullable Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType); final class ConvertiblePair { private final Class<?> sourceType; private final Class<?> targetType; public ConvertiblePair(Class<?> sourceType, Class<?> targetType) { this.sourceType = sourceType; this.targetType = targetType; } } }

看一下 ObjectToOptionalConverter

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
final class ObjectToOptionalConverter implements ConditionalGenericConverter { private final ConversionService conversionService; public ObjectToOptionalConverter(ConversionService conversionService) { this.conversionService = conversionService; } @Override public Set<ConvertiblePair> getConvertibleTypes() { Set<ConvertiblePair> convertibleTypes = new LinkedHashSet<>(4); convertibleTypes.add(new ConvertiblePair(Collection.class, Optional.class)); convertibleTypes.add(new ConvertiblePair(Object[].class, Optional.class)); convertibleTypes.add(new ConvertiblePair(Object.class, Optional.class)); return convertibleTypes; } @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { if (targetType.getResolvableType().hasGenerics()) { return this.conversionService.canConvert(sourceType, new GenericTypeDescriptor(targetType)); } else { return true; } } @Override public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { if (source == null) { return Optional.empty(); } else if (source instanceof Optional) { return source; } else if (targetType.getResolvableType().hasGenerics()) { Object target = this.conversionService.convert(source, sourceType, new GenericTypeDescriptor(targetType)); if (target == null || (target.getClass().isArray() && Array.getLength(target) == 0) || (target instanceof Collection && ((Collection<?>) target).isEmpty())) { return Optional.empty(); } return Optional.of(target); } else { return Optional.of(source); } } @SuppressWarnings("serial") private static class GenericTypeDescriptor extends TypeDescriptor { public GenericTypeDescriptor(TypeDescriptor typeDescriptor) { super(typeDescriptor.getResolvableType().getGeneric(), null, typeDescriptor.getAnnotations()); } } }

这里借助了 ConversionService 协助将 source 对象转换为 Optional<T> 中 T 泛型对象

可以看到 getConvertibleTypes 返回的是三个转换关系的。

ConversionService

作为整个转换系统的入口

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface ConversionService { boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType); boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType); @Nullable <T> T convert(@Nullable Object source, Class<T> targetType); @Nullable Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType); }

如果是泛型相关建议使用 TypeDescriptor 参数的 converter 方法

微信公众号:CoderLi

我们看一下 ConverterRegistry

微信公众号:CoderLi

ConfigurableConversionService

ConfigurableConversionService 只是做了两个接口的整合、啥都没做

复制代码
1
2
3
4
public interface ConfigurableConversionService extends ConversionService, ConverterRegistry { }

GenericConversionService

GenericConversionServiceConversionServiceConverterRegistry 的实现类

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
public class GenericConversionService implements ConfigurableConversionService { // 当不需要进行转换的时候使用该转换器、也就是源对象的类型是目标类型或者其子类 private static final GenericConverter NO_OP_CONVERTER = new NoOpConverter("NO_OP"); // 没有找到合适的转换器、使用该转换器作为占位符、放在缓存 Map 中占位 private static final GenericConverter NO_MATCH = new NoOpConverter("NO_MATCH"); // 内部类、用来管理 Converter 的 private final Converters converters = new Converters(); // 缓存、可以看到 value 是 GenericConverter private final Map<ConverterCacheKey, GenericConverter> converterCache = new ConcurrentReferenceHashMap<>(64); @Override public void addConverter(Converter<?, ?> converter) { ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class); // 转为 GenericConverter addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1])); } @Override public <S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter) { // 转为 GenericConverter addConverter(new ConverterAdapter( converter, ResolvableType.forClass(sourceType), ResolvableType.forClass(targetType))); } @Override public void addConverter(GenericConverter converter) { this.converters.add(converter); invalidateCache(); } @Override public void addConverterFactory(ConverterFactory<?, ?> factory) { // 获取声明的泛型信息 ResolvableType[] typeInfo = getRequiredTypeInfo(factory.getClass(), ConverterFactory.class); ........ // 转为 GenericConverter addConverter(new ConverterFactoryAdapter(factory, new ConvertiblePair(typeInfo[0].toClass(), typeInfo[1].toClass()))); } @Override public void removeConvertible(Class<?> sourceType, Class<?> targetType) { this.converters.remove(sourceType, targetType); invalidateCache(); } @Override public boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType) { Assert.notNull(targetType, "Target type to convert to cannot be null"); 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; } GenericConverter converter = getConverter(sourceType, targetType); return (converter == NO_OP_CONVERTER); } @Override @SuppressWarnings("unchecked") @Nullable public <T> T convert(@Nullable Object source, Class<T> targetType) { Assert.notNull(targetType, "Target type to convert to cannot be null"); return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType)); } @Override @Nullable public Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) { ....... 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); }

DefaultConversionService

DefaultConversionService 提供方法为 ConverterRegistry 增加一些常用的 Converter

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public class DefaultConversionService extends GenericConversionService { @Nullable private static volatile DefaultConversionService sharedInstance; public DefaultConversionService() { addDefaultConverters(this); } public static ConversionService getSharedInstance() { DefaultConversionService cs = sharedInstance; if (cs == null) { synchronized (DefaultConversionService.class) { cs = sharedInstance; if (cs == null) { cs = new DefaultConversionService(); sharedInstance = cs; } } } return cs; } public static void addDefaultConverters(ConverterRegistry converterRegistry) { addScalarConverters(converterRegistry); addCollectionConverters(converterRegistry); converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry)); converterRegistry.addConverter(new StringToTimeZoneConverter()); converterRegistry.addConverter(new ZoneIdToTimeZoneConverter()); converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter()); converterRegistry.addConverter(new ObjectToObjectConverter()); converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry)); converterRegistry.addConverter(new FallbackObjectToStringConverter()); converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry)); } public static void addCollectionConverters(ConverterRegistry converterRegistry) { ConversionService conversionService = (ConversionService) converterRegistry; converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService)); converterRegistry.addConverter(new CollectionToArrayConverter(conversionService)); converterRegistry.addConverter(new ArrayToArrayConverter(conversionService)); converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService)); converterRegistry.addConverter(new MapToMapConverter(conversionService)); converterRegistry.addConverter(new ArrayToStringConverter(conversionService)); converterRegistry.addConverter(new StringToArrayConverter(conversionService)); converterRegistry.addConverter(new ArrayToObjectConverter(conversionService)); converterRegistry.addConverter(new ObjectToArrayConverter(conversionService)); converterRegistry.addConverter(new CollectionToStringConverter(conversionService)); converterRegistry.addConverter(new StringToCollectionConverter(conversionService)); converterRegistry.addConverter(new CollectionToObjectConverter(conversionService)); converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService)); converterRegistry.addConverter(new StreamConverter(conversionService)); } private static void addScalarConverters(ConverterRegistry converterRegistry) { converterRegistry.addConverterFactory(new NumberToNumberConverterFactory()); converterRegistry.addConverterFactory(new StringToNumberConverterFactory()); converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter()); converterRegistry.addConverter(new StringToCharacterConverter()); converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter()); converterRegistry.addConverter(new NumberToCharacterConverter()); converterRegistry.addConverterFactory(new CharacterToNumberFactory()); converterRegistry.addConverter(new StringToBooleanConverter()); converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter()); converterRegistry.addConverterFactory(new StringToEnumConverterFactory()); converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry)); converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory()); converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry)); converterRegistry.addConverter(new StringToLocaleConverter()); converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter()); converterRegistry.addConverter(new StringToCharsetConverter()); converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter()); converterRegistry.addConverter(new StringToCurrencyConverter()); converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter()); converterRegistry.addConverter(new StringToPropertiesConverter()); converterRegistry.addConverter(new PropertiesToStringConverter()); converterRegistry.addConverter(new StringToUUIDConverter()); converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter()); } }

FormattingConversionService

FormattingConversionService 则实现了 FormatterRetistry 接口

微信公众号:CoderLi

微信公众号:CoderLi

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/** * Prints objects of type T for display. * * @author Keith Donald * @since 3.0 * @param <T> the type of object this Printer prints */ @FunctionalInterface public interface Printer<T> { /** * Print the object of type T for display. * @param object the instance to print * @param locale the current user locale * @return the printed text string */ String print(T object, Locale locale); }

Printer 主要是为了打印展示某个类型对象的、根据 locale

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/** * Parses text strings to produce instances of T. * * @author Keith Donald * @since 3.0 * @param <T> the type of object this Parser produces */ @FunctionalInterface public interface Parser<T> { /** * Parse a text String to produce a T. * @param text the text string * @param locale the current user locale * @return an instance of T * @throws ParseException when a parse exception occurs in a java.text parsing library * @throws IllegalArgumentException when a parse exception occurs */ T parse(String text, Locale locale) throws ParseException; }

Parser 则是从 String 根据 locale 解释为 T、这个跟 Converter 优点类似、但是是根据 Locale 的不用来解释转换的

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
/** * Formats objects of type T. * A Formatter is both a Printer <i>and</i> a Parser for an object type. * * @author Keith Donald * @since 3.0 * @param <T> the type of object this Formatter formats */ public interface Formatter<T> extends Printer<T>, Parser<T> { }

Formatter 则直接整合这两个接口

微信公众号:CoderLi

微信公众号:CoderLi

无论是 Printer 或者是 Parser 最终都会转换为 GenericConverter、在 convert 方法中在调用对应的方法进行转换解释。

ApplicationConversionService

Spring Boot 默认使用的 ConversionService 。

但是在 Spring 中管理的却是 WebConversionService。它最终也会调用 addBeans 方法

微信公众号:CoderLi

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
public class ApplicationConversionService extends FormattingConversionService { private static volatile ApplicationConversionService sharedInstance; public ApplicationConversionService() { this(null); } public ApplicationConversionService(StringValueResolver embeddedValueResolver) { if (embeddedValueResolver != null) { setEmbeddedValueResolver(embeddedValueResolver); } configure(this); } public static ConversionService getSharedInstance() { ApplicationConversionService sharedInstance = ApplicationConversionService.sharedInstance; if (sharedInstance == null) { synchronized (ApplicationConversionService.class) { sharedInstance = ApplicationConversionService.sharedInstance; if (sharedInstance == null) { sharedInstance = new ApplicationConversionService(); ApplicationConversionService.sharedInstance = sharedInstance; } } } return sharedInstance; } public static void configure(FormatterRegistry registry) { DefaultConversionService.addDefaultConverters(registry); DefaultFormattingConversionService.addDefaultFormatters(registry); addApplicationFormatters(registry); addApplicationConverters(registry); } public static void addApplicationConverters(ConverterRegistry registry) { addDelimitedStringConverters(registry); registry.addConverter(new StringToDurationConverter()); registry.addConverter(new DurationToStringConverter()); registry.addConverter(new NumberToDurationConverter()); registry.addConverter(new DurationToNumberConverter()); registry.addConverter(new StringToPeriodConverter()); registry.addConverter(new PeriodToStringConverter()); registry.addConverter(new NumberToPeriodConverter()); registry.addConverter(new StringToDataSizeConverter()); registry.addConverter(new NumberToDataSizeConverter()); registry.addConverter(new StringToFileConverter()); registry.addConverter(new InputStreamSourceToByteArrayConverter()); registry.addConverterFactory(new LenientStringToEnumConverterFactory()); registry.addConverterFactory(new LenientBooleanToEnumConverterFactory()); } public static void addDelimitedStringConverters(ConverterRegistry registry) { ConversionService service = (ConversionService) registry; registry.addConverter(new ArrayToDelimitedStringConverter(service)); registry.addConverter(new CollectionToDelimitedStringConverter(service)); registry.addConverter(new DelimitedStringToArrayConverter(service)); registry.addConverter(new DelimitedStringToCollectionConverter(service)); } public static void addApplicationFormatters(FormatterRegistry registry) { registry.addFormatter(new CharArrayFormatter()); registry.addFormatter(new InetAddressFormatter()); registry.addFormatter(new IsoOffsetFormatter()); } public static void addBeans(FormatterRegistry registry, ListableBeanFactory beanFactory) { Set<Object> beans = new LinkedHashSet<>(); beans.addAll(beanFactory.getBeansOfType(GenericConverter.class).values()); beans.addAll(beanFactory.getBeansOfType(Converter.class).values()); beans.addAll(beanFactory.getBeansOfType(Printer.class).values()); beans.addAll(beanFactory.getBeansOfType(Parser.class).values()); for (Object bean : beans) { if (bean instanceof GenericConverter) { registry.addConverter((GenericConverter) bean); } else if (bean instanceof Converter) { registry.addConverter((Converter<?, ?>) bean); } else if (bean instanceof Formatter) { registry.addFormatter((Formatter<?>) bean); } else if (bean instanceof Printer) { registry.addPrinter((Printer<?>) bean); } else if (bean instanceof Parser) { registry.addParser((Parser<?>) bean); } } } }

并且会将所有的 GenericConverter、Converter、Printer、Parser bean 注册到 FormatterRegistry 中

Converter VS PropertyEditor

  • PropertyEditor 从 String 转为其他类型
  • Converter 从各种类型转为各种类型

Spring 同时支持两者。

BeanWrapper 实现了 TypeConverter、而 TypeConverter 则 先使用PropertyEditor转换器器转换,如果没找到对应的转换器器,会⽤ConversionService来进⾏行行对象转换。将两者整合起来做转换。

ConditionalConverter

复制代码
1
2
3
4
5
6
public interface ConditionalConverter { boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); } public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter { }

微信公众号:CoderLi

当你实现了 Converter 接口和 ConditionalConverter 接口的时候、在 ConverterAdapter 适配类中会回调你的 matchs 方法

还有一种情况是

当存在多个一样的 sourceType 和 targetType 的转换器时、怎么选择出一个合适的 Converter

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private static class ConvertersForPair { private final Deque<GenericConverter> converters = new ConcurrentLinkedDeque<>(); public void add(GenericConverter converter) { this.converters.addFirst(converter); } @Nullable public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { for (GenericConverter converter : this.converters) { if (!(converter instanceof ConditionalGenericConverter) || ((ConditionalGenericConverter) converter).matches(sourceType, targetType)) { return converter; } } return null; } @Override public String toString() { return StringUtils.collectionToCommaDelimitedString(this.converters); } }

实际问题

封装 RPC 模块过程中、涉及到各种类型的转换、有 json和 PoJo 的转换、也有 xml 和 PoJo 的转换、更有一些自定义协议报文到 PoJo 的转换、而这些 PoJo 具体是什么类型、在 RPC 的底层模块中是不知道的、只有在运行时通过泛型相关的信息获取的

比如 Json 和 PoJo 之间的转换、可能需要两个转换器、一个是 String 到 PoJo 的、一个是 PoJo 到 String

PoJo 到 String 是比较简单的、这里讨论下 String 到 PoJo。

String 到 PoJo 、这个关系貌似就是 1:N 的关系、貌似使用 ConverterFactory 就可以解决

微信公众号:CoderLi

第一个是泛型问题、而且作为 ConverterFactory 需要手动注册到 ConverterRegistry 中、

最终选定 GenericConverter、

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Component public class JsonStringToObjectConverter implements GenericConverter , ConditionalConverter { @Autowired private ObjectMapper objectMapper; @Override public Set<ConvertiblePair> getConvertibleTypes() { return null; } @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { try { if (source instanceof JsonStringMessage) { return objectMapper.readValue(((JsonStringMessage) source).getSource(), targetType.getType()); }else { // impossible throw new IllegalArgumentException("illegal argument"); } } catch (JsonProcessingException e) { throw new IllegalStateException(e); } } @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { return sourceType.getType().equals(JsonStringMessage.class); }

getConvertibleTypes 返回 null 的话、必须实现接口 ConditionalConverter

此 Converter 作为 globalConverter

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void add(GenericConverter converter) { Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes(); if (convertibleTypes == null) { Assert.state(converter instanceof ConditionalConverter, "Only conditional converters may return null convertible types"); this.globalConverters.add(converter); } else { for (ConvertiblePair convertiblePair : convertibleTypes) { getMatchableConverters(convertiblePair).add(converter); } } }

最后

以上就是单身硬币最近收集整理的关于Spring Converter 体系的全部内容,更多相关Spring内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部