前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring MVC自定义参数处理器不生效解决

Spring MVC自定义参数处理器不生效解决

作者头像
心平气和
发布2021-06-10 15:38:05
1.2K0
发布2021-06-10 15:38:05
举报

一、背景

一位同学写了个自定义参数处理类,具体是继承AbstractNamedValueMethodArgumentResolver,即将json字符串参数转换为一个Object,

使用如下:

代码语言:javascript
复制
  @ResponseBody
  @RequestMapping("/test")
  public Object test(@RequestJsonParam("jsonKey")UserVo userVo){
    //具体业务逻辑处理
  }

请求时参数如下:

就一个参数,名为jsonKey,值为:

代码语言:javascript
复制
{"id":"123","name":"edward"}

即加了RequestJsonParam注解后,会将请求中这个key对应的值转为相应的对象,上例中转成UserVo,这个Vo有两个属性,一个是id,另一个是name。

Xml配置如下:

代码语言:javascript
复制

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
            </list>
        </property>
    </bean>
    
<!-- 基于注解驱动 的MVC -->
    <mvc:annotation-driven>
        <mvc:argument-resolvers>
            <bean class="com.oneplus.common.web.mvc.bind.RequestJsonParamMethodArgumentResolver"/>
        </mvc:argument-resolvers>
    </mvc:annotation-driven>

结果是死活不生效,即参数里的值收到请求后全为空。

二、问题解决

经过调试,修改如下问题解决:

代码语言:javascript
复制
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
            </list>
        </property>

        <property name="customArgumentResolvers">
            <list>
                <bean class="com.oneplus.common.web.mvc.bind.RequestJsonParamMethodArgumentResolver"></bean>
            </list>
        </property>
    </bean>

或者把上面的bean去掉也是以解决的。

三、问题分析

为什么不加自定义的bean或在上面自定义Bean中加上customArgumentResolvers才能生效呢,还是要看Spring MVC请求处理流程,我们知道Spring Mvc有个Servelet接管所有请求,它就是DispatcherServlet,它在初始化时,有初步化HandlerAdapter:

代码语言:javascript
复制
private void initHandlerAdapters(ApplicationContext context) {
    this.handlerAdapters = null;

    if (this.detectAllHandlerAdapters) {
      //从容器中获取相应bean的实现,就包括下面要讲的RequestMappingHandlerAdapter
      Map<String, HandlerAdapter> matchingBeans =
          BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
      
      
      if (!matchingBeans.isEmpty()) {
        this.handlerAdapters = new ArrayList<>(matchingBeans.values());
        // We keep HandlerAdapters in sorted order.
        AnnotationAwareOrderComparator.sort(this.handlerAdapters);
      }
    }
    //省略无关代码
    }

这个Adapter后面是用来真正处理请求的,下面是处理请求的doDispatch方法代码:

代码语言:javascript
复制
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {
        processedRequest = checkMultipart(request);
        multipartRequestParsed = (processedRequest != request);

        // Determine handler for the current request.
        mappedHandler = getHandler(processedRequest);

        // Determine handler adapter for the current request.
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

       //省略无关代码

        // Actually invoke the handler.
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

       //省略无关代码
      }

即先找到相应的HandleAdapter,然后调用它的handle方法,其中有一个就是我们上面说的RequestMappingHandlerAdapter,后者最终又会调到RequestMappingHandlerAdapter的invokeHandlerMethod:

代码语言:javascript
复制
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
      WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
      ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

      ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
      
      //加入自定义参数处理器
      if (this.argumentResolvers != null) {
        invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
      }
   }

如果我们不加入自定义的Bean,即只有下面的配置:

代码语言:javascript
复制
 <mvc:annotation-driven>
        <mvc:argument-resolvers>
            <bean class="com.oneplus.common.web.mvc.bind.RequestJsonParamMethodArgumentResolver"/>
        </mvc:argument-resolvers>
    </mvc:annotation-driven>

则上面DispatcherServlet的initHandlerAdapters方法就会取系统的初始化RequestMappingHandlerAdapter,在这里会把customArgumentResolvers参数传进来。

而如果我们加入RequestMappingHandlerAdapter bean的配置:

代码语言:javascript
复制
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
            </list>
        </property>
    </bean>

则DispatcherServlet就会用这个的bean,因此需要自己设置好自定义参数处理器,即加上customArgumentResolvers属性。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-05-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员升级之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档