You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring MVC:GET请求中泛型包装类型参数反序列化异常问题

解决Spring Boot泛型包装类GET参数类型转换问题

问题根源

  1. Java泛型类型擦除导致运行时Wrapper<T>被视为Wrapper<Object>,Spring无法识别泛型参数的实际类型(比如LocalDate)。
  2. GET请求的参数绑定依赖Spring的DataBinder机制,而非Jackson JSON反序列化,所以Jackson的泛型配置对这类场景无效。

解决方案

方案1:自定义GenericConverter(通用适配所有泛型类型)

创建通用转换器,自动识别Wrapper<T>的泛型参数并完成类型转换:

  1. 定义泛型包装类:
public class Wrapper<T> {
    private T value;

    // getter、setter、构造器
    public T getValue() { return value; }
    public void setValue(T value) { this.value = value; }
}
  1. 实现GenericConverter:
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.format.support.DefaultFormattingConversionService;
import java.util.Set;

public class WrapperConverter implements GenericConverter {
    private final DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();

    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Set.of(new ConvertiblePair(String.class, Wrapper.class));
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        if (source == null) return null;
        // 获取Wrapper的泛型参数类型
        TypeDescriptor valueType = targetType.getElementTypeDescriptor(Wrapper.class.getDeclaredField("value"));
        // 利用Spring内置转换服务完成类型转换
        Object convertedValue = conversionService.convert(source.toString(), sourceType, valueType);
        Wrapper<Object> wrapper = new Wrapper<>();
        wrapper.setValue(convertedValue);
        return wrapper;
    }
}
  1. 注册转换器到Spring容器:
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new WrapperConverter());
    }
}

方案2:创建泛型子类(简单直接,适合少量类型)

为每个需要的泛型类型创建Wrapper的子类,让Spring能明确识别目标类型:

// 针对LocalDate的包装子类
public class LocalDateWrapper extends Wrapper<LocalDate> {}

在DTO中使用子类替代泛型类:

public class DemoDto {
    private LocalDateWrapper dateValue;

    // getter、setter
    public LocalDateWrapper getDateValue() { return dateValue; }
    public void setDateValue(LocalDateWrapper dateValue) { this.dateValue = dateValue; }
}

方案3:@InitBinder注册PropertyEditor(单个Controller专用)

如果仅在特定Controller中使用,可通过@InitBinder注册自定义编辑器:

import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestController;
import java.beans.PropertyEditorSupport;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

@RestController
public class DemoController {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Wrapper.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                LocalDate date = LocalDate.parse(text, DateTimeFormatter.ISO_LOCAL_DATE);
                Wrapper<LocalDate> wrapper = new Wrapper<>();
                wrapper.setValue(date);
                setValue(wrapper);
            }
        });
    }

    @GetMapping("/demo")
    public String demo(@ModelAttribute DemoDto dto) {
        LocalDate date = dto.getDateValue().getValue();
        return "Parsed Date: " + date;
    }
}

内容的提问来源于stack exchange,提问作者Florian Patzl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 01:12:08