springboot-springmvc请求参数获取与原理【长文预警,收藏慢啃】

目录

一、参数注解

1.@PathVariable

2.@RequestHeader

3.@RequestParam

4.@CookieValue

5.@RequestBody

6.@RequestAttribute

7.@MatrixVariable

二、参数解析原理

1.DispatcherServlet类的doDispatch方法

2.HandlerAdapter

3.执行目标方法

4.参数解析器-HandlerMethodArgumentResolver

5.返回值处理器

三、使用Servlet API作为参数

1.使用

四、使用Servlet API作为参数原理

1.InvocableHandlerMethod的getMethodArgumentValues方法

2.HttpServletRequest作为参数的参数处理器

五、复杂参数的解析

1.使用

六、复杂参数解析原理

1.还是同样会进入到以上的参数解析方法。

2.参数类型是Map

2.参数类型是Model

七、自定义对象参数

1.使用

八、自定义对象类型参数封装原理

1.还是同样会进入到以上的参数解析方法。

2.ServletModelAttributeMethodProcessor 参数处理器

3.Converters-数据类型转换器

4.自定义Converter类型转换器

九、流程图



一、参数注解

@PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody、@RequestAttribute

import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
public class ParameterTestController {

    /**
     * 数据绑定:页面提交的请求数据(GET、POST)都可以和对象属性进行绑定
     * @param person
     * @return
     */
    @PostMapping("/saveuser")
    public Person saveuser(Person person){
        return person;
    }

    //  car/2/owner/zhangsan
    @GetMapping("/car/{id}/owner/{username}")
    public Map<String,Object> getCar(@PathVariable("id") Integer id,
                                     @PathVariable("username") String name,
                                     @PathVariable Map<String,String> pv,
                                     @RequestHeader("User-Agent") String userAgent,
                                     @RequestHeader Map<String,String> header,
                                     @RequestParam("age") Integer age,
                                     @RequestParam("inters") List<String> inters,
                                     @RequestParam Map<String,String> params,
                                     @CookieValue("_ga") String _ga,
                                     @CookieValue("_ga") Cookie cookie){
        Map<String,Object> map = new HashMap<>();

//        map.put("id",id);
//        map.put("name",name);
//        map.put("pv",pv);
//        map.put("userAgent",userAgent);
//        map.put("headers",header);
        map.put("age",age);
        map.put("inters",inters);
        map.put("params",params);
        map.put("_ga",_ga);
        System.out.println(cookie.getName()+"===>"+cookie.getValue());
        return map;
    }

    @PostMapping("/save")
    public Map postMethod(@RequestBody String content){
        Map<String,Object> map = new HashMap<>();
        map.put("content",content);
        return map;
    }

}

1.@PathVariable

(1) 获取路径变量的值。

@PathVariable("id") Integer id

@PathVariable("username") String name

(2) 获取所有的路径变量,必须使用Map<String,String>

@PathVariable Map<String,String> pv

2.@RequestHeader

(1) 获取请求头

@RequestHeader("User-Agent") String userAgent

(2) 获取所有请求头

@RequestHeader Map<String,String> header

3.@RequestParam

(1) 获取请求参数xxxx?age=18&inters=games&inters=ball

@RequestParam("age") Integer age,

@RequestParam("inters") List<String> inters,

(2) 获取所有请求参数

@RequestParam Map<String,String> params

4.@CookieValue

(1) 获取某个cookie的值

@CookieValue("_ga") String _ga

(2) 获取Cookie类型的cookie

@CookieValue("_ga") Cookie cookie

5.@RequestBody

只能用于post方式。

(1) @RequestBody String content

获取请求体的内容

6.@RequestAttribute

获取请求域中属性的值。

(1) 第一种获取转发参数:

@RequestAttribute(value = "msg",required = false) String msg

@RequestAttribute(value = "code",required = false)Integer code

(2) 第二种获取转发参数

request.getAttribute("msg");

(3) 注意:

@RequestAttribute不可以用Map获取所有的请求域的值。

Attribute更多用于设置值之后跳转页面,用el表达式获取值。

@GetMapping("/goto")
public String goToPage(HttpServletRequest request){
    request.setAttribute("msg","成功了...");
    request.setAttribute("code",200);
    return "forward:/success";  //转发到  /success请求
}

@ResponseBody
@GetMapping("/success")
public Map success(@RequestAttribute(value = "msg",required = false) String msg,
                   @RequestAttribute(value = "code",required = false)Integer code,
                   HttpServletRequest request){
    Object msg1 = request.getAttribute("msg");

    Map<String,Object> map = new HashMap<>();
    Object hello = request.getAttribute("hello");
    Object world = request.getAttribute("world");
    Object message = request.getAttribute("message");
    map.put("reqMethod_msg",msg1);
    map.put("annotation_msg",msg);
    map.put("hello",hello);
    map.put("world",world);
    map.put("message",message);
    return map;
}

7.@MatrixVariable

矩阵变量

(1) 语法: 

请求路径:/cars/sell;low=34;brand=byd,audi,yd

请求路径:/cars/sell;low=34;brand=byd;brand=audi;brand=yd

请求路径:/boss/1;age=20/2;age=10

(2) 使用

* SpringBoot默认是禁用了矩阵变量的功能

* 手动开启:原理。对于路径的处理。UrlPathHelper进行解析。removeSemicolonContent(移除分号内容)支持矩阵变量的。

* 矩阵变量必须有url路径变量才能被解析。

(3) 开启方式

① 第一种,重写匹配规则

@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        // 设置为不移除分号“;”后面的内容。矩阵变量功能就可以生效
        urlPathHelper.setRemoveSemicolonContent(false);
        configurer.setUrlPathHelper(urlPathHelper);
    }
}

② 第二种,直接在容器中添加组件

@Bean
public WebMvcConfigurer webMvcConfigurer(){
    return new WebMvcConfigurer() {

        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
            UrlPathHelper urlPathHelper = new UrlPathHelper();
            // 设置为不移除分号“;”后面的内容。矩阵变量功能就可以生效
            urlPathHelper.setRemoveSemicolonContent(false);
            configurer.setUrlPathHelper(urlPathHelper);
        }
    };
}

(4) 使用代码

// /cars/sell;low=34;brand=byd,audi,yd
@GetMapping("/cars/{path}")
public Map carsSell(@MatrixVariable("low") Integer low,
                    @MatrixVariable("brand") List<String> brand,
                    @PathVariable("path") String path){
    Map<String,Object> map = new HashMap<>();
    map.put("low",low); // 34
    map.put("brand",brand); // byd,audi,yd
    map.put("path",path); // sell
    return map;
}

// /boss/1;age=20/2;age=10
@GetMapping("/boss/{bossId}/{empId}")
public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,
                @MatrixVariable(value = "age",pathVar = "empId") Integer empAge){
    Map<String,Object> map = new HashMap<>();
    map.put("bossAge",bossAge); // 20
    map.put("empAge",empAge); // 10
    return map;
}

(5) 问题:做页面开发,如果cookie被禁用了,如何获取session?

解析:session是由每次请求携带的cookie中的jsessionid定位的,cookie被禁用了,就无法通过cookie查找到对应的session。

解决:使用矩阵变量,发起请求/xxx;jsessionid=xxxxx,把cookie的值使用矩阵变量的方式进行传递。

二、参数解析原理

springmvc处理逻辑参考博文:

springBoot-springMVC请求处理原理_A_art_xiang的博客-CSDN博客

参数解析也是从DispatcherServlet类的doDispatch方法开始研究。

1.DispatcherServlet类的doDispatch方法

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);
        // 找@RequestMapping注解注释的方法,HandlerMapping中找到能处理请求的Handler(Controller.method())
         // Determine handler for the current request.
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }
         // 为当前Handler 找一个适配器 HandlerAdapter; RequestMappingHandlerAdapter
         // Determine handler adapter for the current request.
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

         // Process last-modified header, if supported by the handler.
         String method = request.getMethod();
         boolean isGet = "GET".equals(method);
         if (isGet || "HEAD".equals(method)) {
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }

         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }
         // 真正的执行目标方法
         // Actually invoke the handler.
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }

         applyDefaultViewName(processedRequest, mv);
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
         dispatchException = ex;
      }
      catch (Throwable err) {
         // As of 4.3, we're processing Errors thrown from handler methods as well,
         // making them available for @ExceptionHandler methods and other scenarios.
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Throwable err) {
      triggerAfterCompletion(processedRequest, response, mappedHandler,
            new NestedServletException("Handler processing failed", err));
   }
   finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
         // Instead of postHandle and afterCompletion
         if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         // Clean up any resources used by a multipart request.
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}

2.HandlerAdapter

有四种处理器的适配器

RequestMappingHandlerAdapter支持方法上标注@RequestMapping。

HandlerFunctionAdapter支持函数式编程的。

等等 。

// org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   if (this.handlerAdapters != null) {
      for (HandlerAdapter adapter : this.handlerAdapters) {
         if (adapter.supports(handler)) {
            return adapter;
         }
      }
   }
   throw new ServletException("No adapter for handler [" + handler +
         "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

3.执行目标方法

// Actually invoke the handler.
//DispatcherServlet -- doDispatch中的
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 以上进去之后实际是执行的RequestMappingHandlerAdapter的handleInternal方法
// RequestMappingHandlerAdapter的handleInternal方法
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

   ModelAndView mav;
   checkRequest(request);

   // Execute invokeHandlerMethod in synchronized block if required.
   if (this.synchronizeOnSession) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Object mutex = WebUtils.getSessionMutex(session);
         synchronized (mutex) {    
            mav = invokeHandlerMethod(request, response, handlerMethod);
         }
      }
      else {
         // No HttpSession available -> no mutex necessary
         mav = invokeHandlerMethod(request, response, handlerMethod);
      }
   }
   else {
     // 真正的执行目标方法
      // No synchronization on session demanded at all...
      mav = invokeHandlerMethod(request, response, handlerMethod);
   }

   if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
      if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
         applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
      }
      else {
         prepareResponse(response);
      }
   }

   return mav;
}
// RequestMappingHandlerAdapter的invokeHandlerMethod方法
@Nullable
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);
      // 注册参数解析器-argumentResolvers,有26个。
      if (this.argumentResolvers != null) {
         invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
      }
      // 注册返回值处理器
      if (this.returnValueHandlers != null) {
         invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
      }
      invocableMethod.setDataBinderFactory(binderFactory);
      invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

      ModelAndViewContainer mavContainer = new ModelAndViewContainer();
      mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
      modelFactory.initModel(webRequest, mavContainer, invocableMethod);
      mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

      AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
      asyncWebRequest.setTimeout(this.asyncRequestTimeout);

      WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
      asyncManager.setTaskExecutor(this.taskExecutor);
      asyncManager.setAsyncWebRequest(asyncWebRequest);
      asyncManager.registerCallableInterceptors(this.callableInterceptors);
      asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

      if (asyncManager.hasConcurrentResult()) {
         Object result = asyncManager.getConcurrentResult();
         mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
         asyncManager.clearConcurrentResult();
         LogFormatUtils.traceDebug(logger, traceOn -> {
            String formatted = LogFormatUtils.formatValue(result, !traceOn);
            return "Resume with async result [" + formatted + "]";
         });
         invocableMethod = invocableMethod.wrapConcurrentResult(result);
      }
      // 真正执行目标方法
      invocableMethod.invokeAndHandle(webRequest, mavContainer);
      if (asyncManager.isConcurrentHandlingStarted()) {
         return null;
      }

      return getModelAndView(mavContainer, modelFactory, webRequest);
   }
   finally {
      webRequest.requestCompleted();
   }
}
// ServletInvocableHandlerMethod的invokeAndHandle方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
    // 执行请求,真正的执行controller的目标方法
   Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
   setResponseStatus(webRequest);

   if (returnValue == null) {
      if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
         disableContentCachingIfNecessary(webRequest);
         mavContainer.setRequestHandled(true);
         return;
      }
   }
   else if (StringUtils.hasText(getResponseStatusReason())) {
      mavContainer.setRequestHandled(true);
      return;
   }

   mavContainer.setRequestHandled(false);
   Assert.state(this.returnValueHandlers != null, "No return value handlers");
   try {
      this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
   }
   catch (Exception ex) {
      if (logger.isTraceEnabled()) {
         logger.trace(formatErrorForReturnValue(returnValue), ex);
      }
      throw ex;
   }
}
// InvocableHandlerMethod的invokeForRequest方法
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
    // 获取方法所有参数的值
   Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
   if (logger.isTraceEnabled()) {
      logger.trace("Arguments: " + Arrays.toString(args));
   }
    // 调用目标方法
   return doInvoke(args);
}
// InvocableHandlerMethod的getMethodArgumentValues方法,这才是如何确定目标方法每一个参数的值
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
    // 获取所有参数
   MethodParameter[] parameters = getMethodParameters();
   if (ObjectUtils.isEmpty(parameters)) {
      return EMPTY_ARGS;
   }

   Object[] args = new Object[parameters.length];
    // 遍历所有参数
   for (int i = 0; i < parameters.length; i++) {
      MethodParameter parameter = parameters[i];
      parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
      args[i] = findProvidedArgument(parameter, providedArgs); // 参数名字发现器
      if (args[i] != null) {
         continue;
      }
      if (!this.resolvers.supportsParameter(parameter)) { // 判断所有26个参数解析器是否支持该参数类型
         throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
      }
      try {
         // 
         args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
      }
      catch (Exception ex) {
         // Leave stack trace for later, exception may actually be resolved and handled...
         if (logger.isDebugEnabled()) {
            String exMsg = ex.getMessage();
            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
               logger.debug(formatArgumentError(parameter, exMsg));
            }
         }
         throw ex;
      }
   }
   return args;
}
// HandlerMethodArgumentResolverComposite的getArgumentResolver方法,挨个判断所有参数解析器那个支持解析这个参数
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result == null) {
        for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
            if (resolver.supportsParameter(parameter)) { // 判断是否支持解析
                result = resolver;
                this.argumentResolverCache.put(parameter, result); // 缓存,第一次执行可能会慢,以后就会很快。
                break;
            }
        }
    }
    return result;
}

4.参数解析器-HandlerMethodArgumentResolver

确定将要执行的目标方法的每一个参数的值是什么;

SpringMVC目标方法能写多少种参数类型。取决于参数解析器。

(1)26个参数解析器

 (2)参数解析器实际是个接口

* 判断当前解析器是否支持解析这种参数

* 支持就调用 resolveArgument

最终参数解析时,调用各自 HandlerMethodArgumentResolver 的 resolveArgument 方法即可

5.返回值处理器

有以下返回值可写:

三、使用Servlet API作为参数

WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId

1.使用

@GetMapping("/test")
public String test(HttpServletRequest request){
    return "success";
}

四、使用Servlet API作为参数原理

1.InvocableHandlerMethod的getMethodArgumentValues方法

同上,也会来到InvocableHandlerMethod的getMethodArgumentValues方法,这才是如何确定目标方法每一个参数的值

// InvocableHandlerMethod的getMethodArgumentValues方法,这才是如何确定目标方法每一个参数的值
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
    // 获取所有参数
   MethodParameter[] parameters = getMethodParameters();
   if (ObjectUtils.isEmpty(parameters)) {
      return EMPTY_ARGS;
   }

   Object[] args = new Object[parameters.length];
    // 遍历所有参数
   for (int i = 0; i < parameters.length; i++) {
      MethodParameter parameter = parameters[i];
      parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
      args[i] = findProvidedArgument(parameter, providedArgs); // 参数名字发现器
      if (args[i] != null) {
         continue;
      }
      if (!this.resolvers.supportsParameter(parameter)) { // 判断所有26个参数解析器是否支持该参数类型
         throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
      }
      try {
         //
         args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
      }
      catch (Exception ex) {
         // Leave stack trace for later, exception may actually be resolved and handled...
         if (logger.isDebugEnabled()) {
            String exMsg = ex.getMessage();
            if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
               logger.debug(formatArgumentError(parameter, exMsg));
            }
         }
         throw ex;
      }
   }
   return args;
}
// HandlerMethodArgumentResolverComposite的getArgumentResolver方法,挨个判断所有参数解析器那个支持解析这个参数
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result == null) {
        for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { // 循环判断所有26个参数解析器是否支持该参数类型
            if (resolver.supportsParameter(parameter)) { // 判断是否支持解析
                result = resolver;
                this.argumentResolverCache.put(parameter, result); // 缓存,第一次执行可能会慢,以后就会很快。
                break;
            }
        }
    }
    return result;
}

2.HttpServletRequest作为参数的参数处理器

ServletRequestMethodArgumentResolver 解析以上的部分参数(WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId)

@Override
public boolean supportsParameter(MethodParameter parameter) {
    Class<?> paramType = parameter.getParameterType();
    return (WebRequest.class.isAssignableFrom(paramType) ||
            ServletRequest.class.isAssignableFrom(paramType) ||
            MultipartRequest.class.isAssignableFrom(paramType) ||
            HttpSession.class.isAssignableFrom(paramType) ||
            (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
            Principal.class.isAssignableFrom(paramType) ||
            InputStream.class.isAssignableFrom(paramType) ||
            Reader.class.isAssignableFrom(paramType) ||
            HttpMethod.class == paramType ||
            Locale.class == paramType ||
            TimeZone.class == paramType ||
            ZoneId.class == paramType);
}

@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

   Class<?> paramType = parameter.getParameterType();

   // WebRequest / NativeWebRequest / ServletWebRequest
   if (WebRequest.class.isAssignableFrom(paramType)) {
      if (!paramType.isInstance(webRequest)) {
         throw new IllegalStateException(
               "Current request is not of type [" + paramType.getName() + "]: " + webRequest);
      }
      return webRequest;
   }

   // ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
   if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
      return resolveNativeRequest(webRequest, paramType);
   }

   // HttpServletRequest required for all further argument types
   return resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));
}

五、复杂参数的解析

Map、Model(map、model里面的数据会被放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder

1.使用

@GetMapping("/params")
public String testParam(Map<String,Object> map,
                        Model model,
                        HttpServletRequest request,
                        HttpServletResponse response){
    map.put("hello","world666");
    model.addAttribute("world","hello666");
    request.setAttribute("message","HelloWorld");
    Cookie cookie = new Cookie("c1","v1");
    response.addCookie(cookie);
    return "forward:/success";
}

@ResponseBody
@GetMapping("/success")
public Map success(HttpServletRequest request){
    Map<String,Object> map = new HashMap<>();
    Object hello = request.getAttribute("hello");
    Object world = request.getAttribute("world");
    Object message = request.getAttribute("message");
    map.put("hello",hello);
    map.put("world",world);
    map.put("message",message);
    return map;
}

Map<String,Object> map,  Model model, HttpServletRequest request 都是可以给request域中放数据,可以使用request.getAttribute();获取数据,也可以在页面使用el表达式获取。

六、复杂参数解析原理

1.还是同样会进入到以上的参数解析方法。

2.参数类型是Map

会使用MapMethodProcessor参数解析器。

public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {

    // 判断是否用该参数解析器解析
   @Override
   public boolean supportsParameter(MethodParameter parameter) {
      return Map.class.isAssignableFrom(parameter.getParameterType()) &&
            parameter.getParameterAnnotations().length == 0;
   }

    // 解析
   @Override
   @Nullable
   public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
         NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
      Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
      return mavContainer.getModel(); // 返回一个BindingAwareModelMap,既是Model 也是Map。
   }
}
// ModelAndViewContainer的getModel方法
public ModelMap getModel() {
   if (useDefaultModel()) {
      return this.defaultModel;
   }
   else {
      if (this.redirectModel == null) {
         this.redirectModel = new ModelMap();
      }
      return this.redirectModel;
   }
}

 目标方法执行完成,将所有的数据都放在 ModelAndViewContainer;包含要去的页面地址View。还包含Model数据。

@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
      ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

   modelFactory.updateModel(webRequest, mavContainer);
   if (mavContainer.isRequestHandled()) {
      return null;
   }
   ModelMap model = mavContainer.getModel();
   ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
   if (!mavContainer.isViewReference()) {
      mav.setView((View) mavContainer.getView());
   }
   if (model instanceof RedirectAttributes) {
      Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
      HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
      if (request != null) {
         RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
      }
   }
   return mav;
}

处理派发结果:

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); -> 
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);

InternalResourceView:视图解析流程
@Override
protected void renderMergedOutputModel(
        Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 暴露模型作为请求域属性
    // Expose the model object as request attributes.
    exposeModelAsRequestAttributes(model, request);

    // Expose helpers as request attributes, if any.
    exposeHelpers(request);

    // Determine the path for the request dispatcher.
    String dispatcherPath = prepareForRendering(request, response);

    // Obtain a RequestDispatcher for the target resource (typically a JSP).
    RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
    if (rd == null) {
        throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
                "]: Check that the corresponding file exists within your web application archive!");
    }

    // If already included or response already committed, perform include, else forward.
    if (useInclude(request, response)) {
        response.setContentType(getContentType());
        if (logger.isDebugEnabled()) {
            logger.debug("Including [" + getUrl() + "]");
        }
        rd.include(request, response);
    }

    else {
        // Note: The forwarded resource is supposed to determine the content type itself.
        if (logger.isDebugEnabled()) {
            logger.debug("Forwarding to [" + getUrl() + "]");
        }
        rd.forward(request, response);
    }
}
protected void exposeModelAsRequestAttributes(Map<String, Object> model,
            HttpServletRequest request) throws Exception {
    //model中的所有数据遍历挨个放在请求域中(在页面跳转之前)
    model.forEach((name, value) -> {
        if (value != null) {
            request.setAttribute(name, value);
        }
        else {
            request.removeAttribute(name);
        }
    });
}

2.参数类型是Model

会使用ModelMethodProcessor参数解析器。

public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {

   @Override
   public boolean supportsParameter(MethodParameter parameter) {
      return Model.class.isAssignableFrom(parameter.getParameterType());
   }

   @Override
   @Nullable
   public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
         NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

      Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
      return mavContainer.getModel(); // 跟Map参数一样,也返回一个BindingAwareModelMap,既是Model 也是Map。
   }
}

七、自定义对象参数

可以自动类型转换与格式化,可以级联封装

1.使用

/**
*     姓名: <input name="userName"/> <br/>
*     年龄: <input name="age"/> <br/>
*     生日: <input name="birth"/> <br/>
*     宠物姓名:<input name="pet.name"/><br/>
*     宠物年龄:<input name="pet.age"/>
*/
@Data
public class Person {
    private String userName;
    private Integer age;
    private Date birth;
    private Pet pet;
}

@Data
public class Pet {
    private String name;
    private String age;
}
/**
* 数据绑定:页面提交的请求数据(GET、POST)都可以和对象属性进行绑定
*/
@PostMapping("/saveuser")
public Person saveuser(Person person){
    return person;
}

八、自定义对象类型参数封装原理

1.还是同样会进入到以上的参数解析方法。

2.ServletModelAttributeMethodProcessor 参数处理器

自定义类型的参数,是用ServletModelAttributeMethodProcessor 参数处理器解析的。

// ModelAttributeMethodProcessor的supportsParameter方法(ServletModelAttributeMethodProcessor继承ModelAttributeMethodProcessor)
@Override
public boolean supportsParameter(MethodParameter parameter) {
   return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
         (this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));// 参数是不是简单类型,自定义对象不是简单类型
}

// org.springframework.beans.BeanUtils#isSimpleProperty
public static boolean isSimpleProperty(Class<?> type) {
   Assert.notNull(type, "'type' must not be null");
   return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType()));
}
public static boolean isSimpleValueType(Class<?> type) {
   return (Void.class != type && void.class != type &&
         (ClassUtils.isPrimitiveOrWrapper(type) ||
         Enum.class.isAssignableFrom(type) ||
         CharSequence.class.isAssignableFrom(type) ||
         Number.class.isAssignableFrom(type) ||
         Date.class.isAssignableFrom(type) ||
         Temporal.class.isAssignableFrom(type) ||
         URI.class == type ||
         URL.class == type ||
         Locale.class == type ||
         Class.class == type));
}
// org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

   Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
   Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

   String name = ModelFactory.getNameForParameter(parameter);
   ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
   if (ann != null) {
      mavContainer.setBinding(name, ann.binding());
   }

   Object attribute = null;
   BindingResult bindingResult = null;

   if (mavContainer.containsAttribute(name)) {
      attribute = mavContainer.getModel().get(name);
   }
   else {
      // Create attribute instance
      try {
         attribute = createAttribute(name, parameter, binderFactory, webRequest);
      }
      catch (BindException ex) {
         if (isBindExceptionRequired(parameter)) {
            // No BindingResult parameter -> fail with BindException
            throw ex;
         }
         // Otherwise, expose null/empty value and associated BindingResult
         if (parameter.getParameterType() == Optional.class) {
            attribute = Optional.empty();
         }
         bindingResult = ex.getBindingResult();
      }
   }

   if (bindingResult == null) {
      // Bean property binding and validation;
      // skipped in case of binding failure on construction.
      // attribute就是创建的空实体类对象(Person),webRequest就是原生的request请求。
      // WebDataBinder :web数据绑定器,将请求参数的值绑定到指定的JavaBean里面
      // WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中
      WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
      if (binder.getTarget() != null) {
         if (!mavContainer.isBindingDisabled(name)) {
            // 关键一步,帮我们把请求里的数据绑定到Person对象,执行的是ServletModelAttributeMethodProcessor类的方法。
            // 绑定过程中,有使用到Converter类型转换器,因为http都是string,如果对象有Integer等类型需要进行转换。
            bindRequestParameters(binder, webRequest);
         }
         validateIfApplicable(binder, parameter);
         if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
            throw new BindException(binder.getBindingResult());
         }
      }
      // Value type adaptation, also covering java.util.Optional
      if (!parameter.getParameterType().isInstance(attribute)) {
         attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
      }
      bindingResult = binder.getBindingResult();
   }

   // Add resolved attribute and BindingResult at the end of the model
   Map<String, Object> bindingResultModel = bindingResult.getModel();
   mavContainer.removeAttributes(bindingResultModel);
   mavContainer.addAllAttributes(bindingResultModel);

   return attribute;
}

GenericConversionService类:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(JavaBean -- Integer)(文件上传:byte -- > file)

3.Converters-数据类型转换器

WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次通过反射封装到JavaBean中。

 

4.自定义Converter类型转换器

未来我们可以给WebDataBinder里面放自己的Converter(T就是要转换的类型);

private static final class StringToNumber<T extends Number> implements Converter<String, T>

(1)新需求:假设宠物输入“猫,3”,就以“,”分割,前后参数分别绑定到Pet的name和age。

/**
*     姓名: <input name="userName"/> <br/>
*     年龄: <input name="age"/> <br/>
*     生日: <input name="birth"/> <br/>
*     宠物:<input name="pet"/>
*/
@Data
public class Person {
    private String userName;
    private Integer age;
    private Date birth;
    private Pet pet;
}


@Data
public class Pet {
    private String name;
    private String age;
}

(2)定义config类

import com.cxf.model.Pet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration(proxyBeanMethods = false)
public class WebConfig{

    //WebMvcConfigurer定制化SpringMVC的功能
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void addFormatters(FormatterRegistry registry) {
                // 添加一个String->Pet的类型转换器
                registry.addConverter(new Converter<String, Pet>() {
                    @Override
                    public Pet convert(String source) {
                        // 猫,3
                        if(!StringUtils.isEmpty(source)){
                            Pet pet = new Pet();
                            String[] split = source.split(",");
                            pet.setName(split[0]);
                            pet.setAge(Integer.parseInt(split[1]));
                            return pet;
                        }
                        return null;
                    }
                });
            }
        };
    }
}

(3)测试一下

http://localhost:8080/person?userName=zhangsan&age=18&birth=1992/12/12&pet=mao,3

{"userName":"zhangsan","age":18,"birth":"1992-12-11T16:00:00.000+00:00","pet":{"name":"mao","age":3}}

九、流程图

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃了也弱了。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值