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

Spring Boot 2.7中@RequestParam接收自定义LinkedHashMap子类报类型不匹配

问题:Spring Boot中自定义LinkedHashMap子类作为@RequestParam参数报错

原正常运行代码

@RestController
@RequestMapping("/system/test")
public class SystemUserController {
    @GetMapping
    public ResponseResult index(@RequestParam LinkedHashMap<String, Object> params) {
        return ResponseResult.success();
    }
}

自定义子类后报错

封装LinkedHashMap为自定义子类MyLinkedHashMap后,调用接口会触发参数类型不匹配错误:

public class MyLinkedHashMap extends LinkedHashMap<String, Object> {}

@RestController
@RequestMapping("/system/test")
public class SystemUserController {
    @GetMapping
    public ResponseResult index(@RequestParam MyLinkedHashMap params) {
        return ResponseResult.success();
    }
}

使用环境为Spring Boot 2.7版本,相关问题搜索未获有效解决方案。

报错信息

Controller [com.example.test.TestController]
Method [public java.lang.String com.example.test.TestController.index(com.example.test.MyLinkedHashMap)] with argument values:
 [0] [type=java.util.LinkedHashMap] [value={}] ] with root cause

java.lang.IllegalArgumentException: argument type mismatch
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.26.jar:5.3.26]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.26.jar:5.3.26]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.26.jar:5.3.26]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.26.jar:5.3.26]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.26.jar:5.3.26]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.26.jar:5.3.26]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072) ~[spring-webmvc-5.3.26.jar:5.3.26]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965) ~[spring-webmvc-5.3.26.jar:5.3.26]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.26.jar:5.3.26]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.26.jar:5.3.26]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:502) ~[tomcat-embed-core-9.0.73.jar:4.0.FR]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.26.jar:5.3.26]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:596) ~[tomcat-embed-core-9.0.73.jar:4.0.FR]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.73.jar:9.0.73]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.26.jar:5.3.26]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.26.jar:5.3.26]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.26.jar:5.3.26]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.26.jar:5.3.26]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.26.jar:5.3.26]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.26.jar:5.3.26]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:492) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:389) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.73.jar:9.0.73]
    at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

自定义类功能说明

MyLinkedHashMap包含通用键读取和固定键读取方法,代码如下:

public class MyLinkedHashMap extends LinkedHashMap<String, Object> {
    public int getInt(String key, int defaultValue) {
        Object value = get(key);
        // if(valueIsNull(value)) {
        //     return defaultValue;
        // }
        return Integer.valueOf(String.valueOf(value));
    }


    public int getKeyPageNo() {
        int pageNo = getInt("pageNo", 1);
        if (pageNo <= 0) {
            pageNo = 1;
        }
        return pageNo;
    }

    public int getKeyPageSize() {
        int pageSize = getInt("pageSize", 10);
        if (pageSize <= 0 || pageSize > 1000) {
            pageSize = 10;
        }
        return pageSize;
    }


    // like... any key
    public String getKey(String key) {
        return get(key).toString();
    }
}

解决方案

原因分析

Spring处理@RequestParam的Map类型参数时,默认实例化LinkedHashMap,但不会自动将其转换为自定义子类。反射调用方法时参数类型需严格匹配,因此触发类型不匹配异常。

解决方法1:自定义转换器

让Spring能够将请求参数转换为自定义Map子类:

  1. 确保MyLinkedHashMap存在无参构造器(父类已默认提供,若有自定义构造器需显式声明)。
  2. 创建转换器类:
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.MultiValueMap;

public class MyLinkedHashMapConverter implements Converter<MultiValueMap<String, String>, MyLinkedHashMap> {
    @Override
    public MyLinkedHashMap convert(MultiValueMap<String, String> source) {
        MyLinkedHashMap map = new MyLinkedHashMap();
        source.forEach((key, values) -> {
            if (values.size() == 1) {
                map.put(key, values.get(0));
            } else {
                map.put(key, values);
            }
        });
        return map;
    }
}
  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 MyLinkedHashMapConverter());
    }
}

解决方法2:使用组合而非继承(推荐)

避免继承LinkedHashMap,改用组合方式封装,更符合面向对象设计原则:

public class RequestParamsWrapper {
    private final LinkedHashMap<String, Object> params;

    public RequestParamsWrapper(LinkedHashMap<String, Object> params) {
        this.params = params;
    }

    public int getInt(String key, int defaultValue) {
        Object value = params.get(key);
        if (value == null) {
            return defaultValue;
        }
        try {
            return Integer.parseInt(value.toString());
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }

    public int getKeyPageNo() {
        int pageNo = getInt("pageNo", 1);
        return pageNo <= 0 ? 1 : pageNo;
    }

    public int getKeyPageSize() {
        int pageSize = getInt("pageSize", 10);
        if (pageSize <= 0 || pageSize > 1000) {
            return 10;
        }
        return pageSize;
    }

    public String getKey(String key) {
        Object value = params.get(key);
        return value != null ? value.toString() : null;
    }
}

控制器中使用方式:

@RestController
@RequestMapping("/system/test")
public class SystemUserController {
    @GetMapping
    public ResponseResult index(@RequestParam LinkedHashMap<String, Object> params) {
        RequestParamsWrapper wrapper = new RequestParamsWrapper(params);
        // 调用wrapper的方法,如wrapper.getKeyPageNo()
        return ResponseResult.success();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 20:54:50