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

一、场景复现

1、相关代码

@Bean
public Converter<String, LocalDateTime> localDateTimeConverter() {
	return source -> LocalDateTime.parse(source.trim(), DateTimeFormatter.ofPattern(LOCAL_DATE_TIME_FORMAT));
}

2、报错信息

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

adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());

2. 而ConfigurableWebBindingInitializer需要FormattingConversionService, 而FormattingConversionService会将所有的Converter添加进来

添加的时候需要获取泛型信息

@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);
    }
}

3. 添加Converter.class 一般是通过接口获取两个泛型的具体类型

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);
}

4. Lambda表达式的接口是Converter<?, ?>,不能的到具体的类型

三、解决办法

3.1、具体类型接口,不再是泛型

@Bean
public StringToLocalDateTimeConverter localDateTimeConverter1() {
    return  (source ->  LocalDateTime.parse((String)source, DateTimeUtils.DEFAULT_FORMATTER));
}
interface StringToLocalDateTimeConverter extends Converter<String, LocalDateTime> {
}

3.2、等待requestMappingHandlerAdapter注册结束在注册

就是等requestMappingHandlerAdapterbean注册完成之后再添加自己的converter就不会注册到FormattingConversionService中

@Bean
@ConditionalOnBean(name = "requestMappingHandlerAdapter")
public Converter<String, LocalDateTime> localDateTimeConverter() {
    return source -> LocalDateTime.parse(source, DateTimeUtils.DEFAULT_FORMATTER);
}

2022年8月8日14:15:00修改

如果实在stater中进行使用则应该使用@DependsOn注解,依旧是启动顺序的问题

@Bean
@DependsOn("requestMappingHandlerAdapter")
public Converter<String, Date> dateConverter() {
	return source -> DateUtil.parse(source.trim());
}

3.3、自定义Converter<S,T>接口

其实有点类似第一种解决方案

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

添加Converter解析器中使用lambda表达式代替匿名内部类是启动报错does the class parameterize those types?
https://yangxj96.github.io/2022/12/java/b005/
作者
yangxj96
发布于
2022年12月22日
许可协议