• SpringMVC是怎么把get请求参数封装到对象中的?


    一、问题简介

    如题,请求 http://localhost:8080/api/test?redirectUrl=https%3A%2F%2Fwww.baidu.com%2F&data=123 这样的 URL,Web应用服务器用以下控制器来接收:

    import com.example.demo.dto.ParamMap;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/api")
    public class ClientController {
    
      @RequestMapping("/test")
      public String receive(ParamMap map) {
        System.out.println(map.getRedirectUrl());
        System.out.println(map.getData());
        return "ok";
      }
    
    }
    

    其中,ParamMap 是一个简单的JavaBean对象:

    public class ParamMap {
    
      private String redirectUrl;
    
      private String data;
    
      public String getRedirectUrl() {
        return redirectUrl;
      }
    
      public void setRedirectUrl(String redirectUrl) {
        this.redirectUrl = redirectUrl;
      }
    
      public String getData() {
        return data;
      }
    
      public void setData(String data) {
        this.data = data;
      }
    }
    

    URL 中的参数,是怎样赋值给 ParamMap 的呢?

    二、DispatcherServlet

    根据 Servlet 规范,当 Servlet 容器允许某个 servlet 对象响应请求时,就会调用 javax.servlet.Servletvoid service(ServletRequest request, ServletResponse response) 方法。

    SpringMVC 的核心 DispatcherServlet 正是继承了 javax.servlet.http.HttpServlet ,实现“分发请求给对应处理器”的功能。

    2.1 doDispatch

    DispatcherServlet 分发请求的核心方法正是 doDispatch,其中

    • getHandler 负责寻找与URL路径(如本文中的路径 /api/test)相匹配的处理器
    • getHandlerAdapter 则是寻找处理器对应的适配器
    • mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
      • HTTP请求参数转化为Controller方法参数
      • 触发Controller中方法

    简要的调用顺序如下所示(和 Debug 的调用栈的显示顺序相反):

    doDispatch       -- DispatcherServlet (org.springframework.web.servlet)
    handle           -- AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
    handleInternal   -- RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
    ...
    invokeAndHandle  -- ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
    ...
    resolveArgument  -- HandlerMethodArgumentResolverComposite (org.springframework.web.method.support)
    resolveArgument  -- ModelAttributeMethodProcessor (org.springframework.web.method.annotation)
    

    三、ModelAttributeMethodProcessor

    ModelAttributeMethodProcessorresolveArgument 方法中几处核心调用:

    • attribute = createAttribute(name, parameter, binderFactory, webRequest); 构造一个空对象(比如本文示例中的 ParamMap 对象);
    • WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name); 创建一个 WebDataBinder 对象,该对象将用于把http请求中的数据绑定到 attribute 中;
    • bindRequestParameters(binder, webRequest); 完成 http 请求数据绑定到 attribute 中的操作;

    四、ServletRequestDataBinder

    具体的绑定操作实现,可以参考 ServletRequestDataBinderbind 方法:

    • MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request); 从 ServletRequest 参数创建一个 PropertyValues 实例;
    • doBind 方法把数据存储到对象中

    其中,MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request); 在构造时,有一个父类的构造函数如下:

    public ServletRequestParameterPropertyValues(ServletRequest request, @Nullable String prefix, @Nullable String prefixSeparator) {
      super(WebUtils.getParametersStartingWith(request, (prefix != null ? prefix + prefixSeparator : null)));
    }
    

    其中,这里就用到了 getParameterValues 抽取http请求参数并存储到一个 Map 中:

    public static Map<String, Object> getParametersStartingWith(ServletRequest request, @Nullable String prefix) {
      Assert.notNull(request, "Request must not be null");
      Enumeration<String> paramNames = request.getParameterNames();
      Map<String, Object> params = new TreeMap<>();
      if (prefix == null) {
        prefix = "";
      }
      while (paramNames != null && paramNames.hasMoreElements()) {
        String paramName = paramNames.nextElement();
        if (prefix.isEmpty() || paramName.startsWith(prefix)) {
          String unprefixed = paramName.substring(prefix.length());
          String[] values = request.getParameterValues(paramName);
          if (values == null || values.length == 0) {
            // Do nothing, no values found at all.
          }
          else if (values.length > 1) {
            params.put(unprefixed, values);
          }
          else {
            params.put(unprefixed, values[0]);
          }
        }
      }
      return params;
    }
    
  • 相关阅读:
    shell遍历文件夹并执行命令
    安装PIL的坑
    iptables不小心把127.0.0.1封了,导致redis连不上
    python读取中文
    不要在基类析构函数中调用纯虚函数,否则运行时会报错“pure virtual method called”
    阿里云64位centos6.3系统上编译安装redis
    Git
    Easy Mock
    Sortable
    几个框架
  • 原文地址:https://www.cnblogs.com/kendoziyu/p/15880280.html
Copyright © 2020-2023  润新知