【Spring】SpringBoot添加Converter解析器中使用lambda表达式代替匿名内部类是启动报错: does the class parameterize those types?

本文最后更新于:21 天前

0.场景复现

0.1 相关代码

1
2
3
4
5
6
7
8
9
/**
* 注入自定义的LocalDateTime转换器
*
* @return 时间转换器
*/
@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.原因分析

  1. web项目启动注册requestMappingHandlerAdapter的时候会初始化WebBindingInitializer
1
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
  1. 而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);
}
}
  1. 添加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);
}
  1. 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"))
}

}

【Spring】SpringBoot添加Converter解析器中使用lambda表达式代替匿名内部类是启动报错: does the class parameterize those types?
https://www.yangxj96.com/Spring/SpringLambdaAddConverterError/
作者
道一
发布于
2022年12月24日
许可协议