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

2021-06-10 15:38:05 浏览数 (1)

一、背景

一位同学写了个自定义参数处理类,具体是继承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属性。

0 人点赞