本文最后更新于:21 天前
0.场景复现 0.1 相关代码 1 2 3 4 5 6 7 8 9 @Bean public Converter<String, LocalDateTime> localDateTimeConverter () { return source -> LocalDateTime.parse(source.trim(), DateTimeFormatter.ofPattern(LOCAL_DATE_TIME_FORMAT)); }
0.2 报错信息 1 2 3 Caused by: java.lang.IllegalArgumentException: Unable to determine source type <S> and target type <T> for your Converter [com.example.demo126.config.MappingConverterAdapter $ $Lambda $522 /817994751 ]; does the class parameterize those types ?
1.原因分析
web项目启动注册requestMappingHandlerAdapter的时候会初始化WebBindingInitializer
1 adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
而ConfigurableWebBindingInitializer需要FormattingConversionService, 而FormattingConversionService会将所有的Converter添加进来 添加的时候需要获取泛型信息
1 2 3 4 5 6 7 8 9 10 11 12 @Override public void addFormatters (FormatterRegistry registry) { for (Converter<?, ?> converter : getBeansOfType(Converter.class)) { registry.addConverter(converter); } for (GenericConverter converter : getBeansOfType(GenericConverter.class)) { registry.addConverter(converter); } for (Formatter<?> formatter : getBeansOfType(Formatter.class)) { registry.addFormatter(formatter); } }
添加Converter.class 一般是通过接口获取两个泛型的具体类型,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public ResolvableType as (Class<?> type) { if (this == NONE) { return NONE; } Class<?> resolved = resolve(); if (resolved == null || resolved == type) { return this ; } for (ResolvableType interfaceType : getInterfaces()) { ResolvableType interfaceAsType = interfaceType.as(type); if (interfaceAsType != NONE) { return interfaceAsType; } } return getSuperType().as(type); }
Lambda表达式的接口是Converter, ?>,不能得到具体的类型
2.解决办法 2.1、具体类型接口,不再是泛型 1 2 3 4 5 6 7 8 @Bean public StringToLocalDateTimeConverter localDateTimeConverter1 () { return (source -> LocalDateTime.parse((String)source, DateTimeUtils.DEFAULT_FORMATTER)); }interface StringToLocalDateTimeConverter extends Converter <String, LocalDateTime> { }
2.2、等待requestMappingHandlerAdapter注册结束在注册 此方法在Spring Boot 3.X之后失效. 暂不清楚原因
就是等requestMappingHandlerAdapterbean注册完成之后再添加自己的converter就不会注册到FormattingConversionService中
1 2 3 4 5 @Bean @ConditionalOnBean(name = "requestMappingHandlerAdapter") public Converter<String, LocalDateTime> localDateTimeConverter () { return source -> LocalDateTime.parse(source, DateTimeUtils.DEFAULT_FORMATTER); }
如果实在stater中进行使用则应该使用@DependsOn注解,依旧是启动顺序的问题
1 2 3 4 5 @Bean @DependsOn("requestMappingHandlerAdapter") public Converter<String, Date> dateConverter () { return source -> DateUtil.parse(source.trim()); }
2.3、自定义Converter<S,T>接口
其实有点类似第一种解决方案
1 2 3 4 5 6 @FunctionalInterface public interface Converter <S, T> { T convert (S source) ; }
如下,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Configuration class StringToLocalDateConverter : Converter<String, LocalDate> { @Resource private lateinit var props: JacksonProperties override fun convert(source: String): LocalDate? { if (StrUtil.isBlank(source)) { return null } return LocalDate.parse(source.trim { it <= ' ' }, DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss")) } }