> 文章列表 > 10、请求参数处理

10、请求参数处理

10、请求参数处理

文章目录

  • 1、请求映射
    • 1.1、REST使用与原理
      • 1、介绍
      • 2、SpringMVC使用
      • 3、Spring Boot使用
      • 4、测试REST
      • 5、REST原理(表单提交使用REST的时候)
      • 6、扩展:如何把_method 这个名字换成自定义名字。
    • 1.2、请求映射原理
      • 1、DispatcherServlet 的继承关系与功能的实现
      • 2、分析 doDispatch 方法
      • 3、分析 getHandler 方法
      • 4、RequestMappingHandlerMapping 处理器映射器
      • 5、获取处理器
      • 6、分析 getHandlerInternal
      • 7、分析 getHandlerInternal
      • 8、分析 lookupHandlerMethod
      • 9、总结
  • 2、普通参数与基本注解
    • 2.1、注解
      • 1、介绍
      • 2、练习
    • 2.2、Servlet API
      • 1、缓存参数解析器
      • 2、匹配规则
      • 3、参数处理器处理细节
      • 4、resolveNativeRequest 方法
    • 2.3、复杂参数
      • 1、Map、Model、HttpServletRequest、HttpServletResponse
      • 2、RedirectAttributes
      • 3、ServletResponse
    • 2.4、源码解析
      • 1、获取参数解析器
      • 2、MapMethodProcessor
      • 3、ModelMethodProcessor
      • 4、数据放入请求域中
    • 2.5、自定义对象参数
  • 3、POJO封装过程
  • 4、参数原理
    • 4.1、思路
    • 4.2、进入 DispatcherServlet 类
    • 4.3、进入 getHandler 方法
    • 4.4、进入 getHandlerAdapter 方法
      • 1、进入 supports 方法
      • 2、回到 getHandlerAdapter 方法
      • 3、回到 doDispatch 方法
    • 4.5、调用执行目标方法
      • 1、进入 handle 方法
      • 2、接口
      • 3、进入 handleInternal 方法
    • 4.6、准备执行目标方法
      • 1、进入 invokeHandlerMethod 方法
      • 2、参数解析器
      • 3、返回值处理器
      • 4、返回 invocableMethod 类
    • 4.7、执行目标方法
      • 1、进入 invokeAndHandle 方法
      • 2、真正执行目标方法
    • 4.8、获取方法参数值
      • 1、进入 getMethodArgumentValues 方法
      • 2、supportsParameter
      • 3、回到 getMethodArgumentValues 方法
      • 4、进入 resolveArgument 方法
    • 4.9、参数解析器
      • 1、进入 resolveArgument 方法
      • 2、进入 resolveName 方法
      • 3、循环解析所有参数
      • 4、getMethodArgumentValues 方法结束
    • 4.10、自定义类型参数 POJO
    • 4.11、域对象共享
      • 1、回到 invokeAndHandle 方法
      • 2、处理返回值
      • 3、进入 handleReturnValue 方法
    • 4.12、继续执行
      • 1、回到 invokeHandlerMethod方法
      • 2、进入 getModelAndView 方法
      • 3、进入 updateModel 方法
      • 4、进入 updateBindingResult 方法
      • 5、回到 handleInternal 方法
      • 6、回到 doDispatch 方法
      • 7、进入 processDispatchResult 方法
      • 8、解析视图

【尚硅谷】SpringBoot2零基础入门教程-讲师:雷丰阳
笔记

路还在继续,梦还在期许

1、请求映射

编写controller类,类上写 @Controller注解,在类中每一个方法上写 @RequestMapping注解,声明当前方法可以处理的请求,这个声明过程称为请求映射

请求映射中常用的方式是@RequestMapping注解。

开发常使用REST风格编写请求映射。

1.1、REST使用与原理

1、介绍

以前:路径

/getUser:获取用户
/deleteUser:删除用户
/editUser:修改用户
/saveUser:保存用户

现在:路径+请求方式(使用HTTP请求方式动词来表示对资源的操作)

/user:GET-获取用户
/user:DELETE-删除用户
/user:PUT-修改用户
/user:POST-保存用户

2、SpringMVC使用

浏览器只能发送GET或POST请求,需要后端配合使用过滤器,对请求进行包装。

在web.xml文件中配置过滤器(HiddenHttpMethodFilter)。

使用POST方式提交表单,表单中写隐藏域 _method=请求方式

3、Spring Boot使用

spring boot 已经为用户自动配置 HiddenHttpMethodFilter

位置:org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

@Bean
// 容器中没有HiddenHttpMethodFilter才自动配置
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
// 获取配置文件中配置属性,确定开启REST风格(配置文件中获取不到属性就不开启)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {return new OrderedHiddenHttpMethodFilter();
}

在页面使用表单提交,需要表单以POST方式提交,表单中写隐藏域 _method=请求方式

在配置文件中开启REST风格(用于接收页面表单提交数据)

spring:mvc:hiddenmethod:filter:enabled: true #开启REST风格

4、测试REST

测试页面

测试REST风格
<form action="/user" method="get"><input value="REST-GET 提交" type="submit"/>
</form>
<form action="/user" method="post"><input value="REST-POST 提交" type="submit"/>
</form>
<form action="/user" method="post"><input type="hidden" name="_method" value="delete"><input value="REST-DELETE 提交" type="submit"/>
</form>
<form action="/user" method="post"><input type="hidden" name="_method" value="put"><input value="REST-PUT 提交" type="submit"/>
</form>

controller

@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){return "GET-张三";
}@RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){return "POST-张三";
}@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){return "PUT-张三";
}@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){return "DELETE-张三";
}

5、REST原理(表单提交使用REST的时候)

  • 表单提交会带上_method=PUT
  • 请求过来被HiddenHttpMethodFilter拦截
    • 请求是POST,且无异常抛出
    • 获取到_method的值
    • 兼容以下请求:PUT、DELETE、PATCH
    • 将原生 POST 方式 提交的 request() 请求 ,经过包装模式 requesWrapper ,重写了getMethod方法,更改提交方式
    • 过滤器链放行的时候用wrapper,以后的方法调用 getMethod 是调用 requesWrapper 包装过的请求方式

位置:org.springframework.web.filter.HiddenHttpMethodFilter

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {HttpServletRequest requestToUse = request;// POST请求方式且程序无异常,如果发送PUT或DELETE直接放行if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {String paramValue = request.getParameter(this.methodParam);if (StringUtils.hasLength(paramValue)) {String method = paramValue.toUpperCase(Locale.ENGLISH);if (ALLOWED_METHODS.contains(method)) {requestToUse = new HttpMethodRequestWrapper(request, method);}}}filterChain.doFilter(requestToUse, response);
}

Rest使用客户端工具直接发送,就和以上做表单的无关了。

比如 安卓 可以直接发送PUT、DELETE。

或 Postman 直接发送PUT、DELETE等方式请求,无需Filter。

所以 spring boot 中的 REST风格功能是选择性开启。

6、扩展:如何把_method 这个名字换成自定义名字。

//在spring boot 配置类中 自定义 hiddenHttpMethodFilter
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();// 改成自定义名字methodFilter.setMethodParam("_m");return methodFilter;
}

1.2、请求映射原理

1、DispatcherServlet 的继承关系与功能的实现

梦回 SpringMVC ,重新讲了一遍 SpringMVC 中 DispatcherServlet 的继承关系与功能的实现。

10、请求参数处理
SpringMVC 功能分析都从 org.springframework.web.servlet.DispatcherServlet 内 doDispatch()方法开始。

2、分析 doDispatch 方法

位置:org.springframework.web.servlet.DispatcherServlet(IDEA查询文件快捷键:Ctrl+Shift+N)

// 请求作派发
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 包装原生请求HttpServletRequest processedRequest = request;// Handler执行链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.// 找到当前请求使用哪个Handler(Controller的方法)处理mappedHandler = getHandler(processedRequest);     

3、分析 getHandler 方法

位置:org.springframework.web.servlet.DispatcherServlet(IDEA查询文件快捷键:Ctrl+Shift+N)

进入 getHandler 方法

/* Return the HandlerExecutionChain for this request.* <p>Tries all handler mappings in order.* @param request current HTTP request* @return the HandlerExecutionChain, or {@code null} if no handler could be found*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {// 获取到所有的 HandlerMapping(处理器映射器)// 处理器映射规则:/请求--->>交给controller中方法处理if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);// 获取处理器HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;
}

获取到的五个默认的HandlerMapping

10、请求参数处理

WelcomePageHandIerMapping:保存了首页访问规则

4、RequestMappingHandlerMapping 处理器映射器

作用:保存了所有@RequestMapping 和handler的映射规则。

10、请求参数处理

5、获取处理器

根据RequestMappingHandlerMapping 处理器映射器中的映射规则获取处理器

位置:org.springframework.web.servlet.handler.AbstractHandlerMapping

/* Look up a handler for the given request, falling back to the default* handler if no specific one is found.* @param request current HTTP request* @return the corresponding handler instance, or the default handler* @see #getHandlerInternal*/
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {// 获取处理器Object handler = getHandlerInternal(request);if (handler == null) {handler = getDefaultHandler();}if (handler == null) {return null;}// Bean name or resolved handler?if (handler instanceof String) {String handlerName = (String) handler;handler = obtainApplicationContext().getBean(handlerName);}HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);if (logger.isTraceEnabled()) {logger.trace("Mapped to " + handler);}else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {logger.debug("Mapped to " + executionChain.getHandler());}if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);config = (config != null ? config.combine(handlerConfig) : handlerConfig);executionChain = getCorsHandlerExecutionChain(request, executionChain, config);}return executionChain;
}

6、分析 getHandlerInternal

位置:org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);try {return super.getHandlerInternal(request);}finally {ProducesRequestCondition.clearMediaTypesAttribute(request);}
}

7、分析 getHandlerInternal

位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping

// Handler method lookup/* Look up a handler method for the given request.*/
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {// 拿到访问路径String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);request.setAttribute(LOOKUP_PATH, lookupPath);// 拿到一把锁,防止并发问题this.mappingRegistry.acquireReadLock();try {HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);}finally {this.mappingRegistry.releaseReadLock();}
}

8、分析 lookupHandlerMethod

位置:org.springframework.web.servlet.handler.AbstractHandlerMethodMapping

/* Look up the best-matching handler method for the current request.* If multiple matches are found, the best match is selected.* @param lookupPath mapping lookup path within the current servlet mapping* @param request the current request* @return the best-matching handler method, or {@code null} if no match* @see #handleMatch(Object, String, HttpServletRequest)* @see #handleNoMatch(Set, String, HttpServletRequest)*/
@Nullable
// lookupPath:当前要查询的路径  request:原生请求
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {List<Match> matches = new ArrayList<>();// 通过路径查询那个控制器方法可以处理List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);// 查询到4个if (directPathMatches != null) {// 添加到匹配的集合中addMatchingMappings(directPathMatches, matches, request);}// 找不到就填写一些空的东西if (matches.isEmpty()) {// No choice but to go through all mappings...addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);}// 找到不为空if (!matches.isEmpty()) {// 获取找到的第一个,并认为第一个是最佳匹配项Match bestMatch = matches.get(0);// matches.size() > 1:同时找到多个if (matches.size() > 1) {Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));matches.sort(comparator);bestMatch = matches.get(0);if (logger.isTraceEnabled()) {logger.trace(matches.size() + " matching mappings: " + matches);}if (CorsUtils.isPreFlightRequest(request)) {return PREFLIGHT_AMBIGUOUS_MATCH;}Match secondBestMatch = matches.get(1);if (comparator.compare(bestMatch, secondBestMatch) == 0) {Method m1 = bestMatch.handlerMethod.getMethod();Method m2 = secondBestMatch.handlerMethod.getMethod();String uri = request.getRequestURI();// 经过匹配排序,如果两个方法都可以处理同一个请求,抛出异常throw new IllegalStateException("Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");}}request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);handleMatch(bestMatch.mapping, lookupPath, request);return bestMatch.handlerMethod;}else {return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);}
}

9、总结

  • SpringBoot 自动配置欢迎页的 WelcomePageHandlerMapping,访问 /能访问到index.html

  • SpringBoot 自动配置了默认 的 RequestMappingHandlerMapping

  • 请求进来,挨个尝试所有的 HandlerMapping 看是否有请求信息

    • 如果有就找到这个请求对应的handler
    • 如果没有就是下一个 HandlerMapping
  • 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping

2、普通参数与基本注解

重新讲了 SpringMVC 获取的请求参数,加入 springboot 矩阵模型和 request域对象共享。

2.1、注解

1、介绍

@PathVariable(路径变量与控制器方法的形参绑定,配合REST风格使用)

如果是表单POST提交,需要手动开启

spring:mvc:hiddenmethod:filter:enabled: true

@RequestHeader(请求头信息与控制器方法的形参绑定)

@RequestParam(设置与形参绑定的请求参数的名称)

@CookieValue(cookie数据和控制器方法的形参绑定)

@RequestBody(让控制器方法返回字符串,配合JSON使用)

@RequestAttribute(获取request域属性)

@MatrixVariable(矩阵变量)

spring boot默认禁用了矩阵变量,需要手动开启

手动开启:对于路径的处理,使用UrlPathHelper进行解析(原理)

位置:org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter

// 配置路径映射
@Override
@SuppressWarnings("deprecation")
public void configurePathMatch(PathMatchConfigurer configurer) {configurer.setUseSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseSuffixPattern());configurer.setUseRegisteredSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern());this.dispatcherServletPath.ifAvailable((dispatcherPath) -> {String servletUrlMapping = dispatcherPath.getServletUrlMapping();if (servletUrlMapping.equals("/") && singleDispatcherServlet()) {// URL的路径帮助器UrlPathHelper urlPathHelper = new UrlPathHelper();urlPathHelper.setAlwaysUseFullPath(true);configurer.setUrlPathHelper(urlPathHelper);}});
}

位置:org.springframework.web.util.UrlPathHelper

public class UrlPathHelper {private boolean removeSemicolonContent = true;/* Set if ";" (semicolon) content should be stripped from the request URI.* <p>Default is "true".*/public void setRemoveSemicolonContent(boolean removeSemicolonContent) {checkReadOnly();this.removeSemicolonContent = removeSemicolonContent;}
}

UrlPathHelper中removeSemicolonContent(移除分号内容)属性,默认是true,不支持矩阵变量的方式,矩阵变量必须有url路径变量分号后内容才能被解析,将这个属性通过配置文件改为false。

配置类

@Configuration
public class WebConfig {/* 方式一* 添加 WebMvcConfigurer 组件*/@Beanpublic WebMvcConfigurer webMvcConfigurer() {return new WebMvcConfigurer() {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();// 不移出路径变量中;后的内容(矩阵变量就可以使用了)urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}};}/* 方式二* 实现WebMvcConfigurer接口,jdk8接口有默认实现,只需要重写configurePathMatch即可* 重写内容与方式一相同*/
}

2、练习

页面

<h2>测试基本注解:</h2>
<ul><li><a href="/car/3/owner/lisi">@PathVariable(路径变量)</a></li><li><a href="/headers">@RequestHeader(获取请求头)</a></li><li><a href="/param?age=18&inters=basketball&inters=game">@RequestParam(获取请求参数)</a></li><li><a href="/setcookie">向浏览器响应一个JSESSIONID的Cookie</a></li><li><a href="cookies">@CookieValue(获取cookie值)</a></li><li><a href="">@RequestBody(获取请求体的值,在下面表单中测试)</a></li><li><a href="">@RequestAttribute(获取request域属性)</a></li><li><a href="">@MatrixVariable(矩阵变量)</a></li>
</ul>
<h2>矩阵变量:</h2>
<h3>页面开发,cookie禁用了,session里面的内容怎么使用?</h3>
<p>cookie未被禁用的使用</p><br/>
<p>服务端向session域中传值:session.set(a,b),将 JSESSIONID 封装进入 cookie ,通过浏览器每次访问携带cookie,实现session的会话共享 </p><br/>
<p>cookie被禁用的使用</p><br/>
<p>使用矩阵变量的方式进行传递,url:/abc;JSESSIONID=xxx</p>
<ul><li><a href="/cars/sell;low=34;brand=byd,audi,yd">@matrixvariab1e(矩阵变量)</a></li><li><a href="/cars/sell;low=34;brand=byd;brand=audi;brand=yd">@MatrixVariab1e(矩阵变量)</a></li><li><a href="/boss/1;age=20/2;age=10">@Matrixvariab1e(矩阵变量)/boss/{bossId}/{empId}</a></li>
</ul><h2>获取请求体的值</h2>
<form action="/save" method="post">测试@RequestBody获取数据<br/>用户名:<input name="userName"/> <br>邮箱:<input name="email"/><input type="submit" value="提交"/>
</form>
<ol><li> 矩阵变量需要在 SpringBoot 中手动开启</li><li> 根据 RFC3986 的规范,矩阵变量应当绑定在路径变量中!</li><li> 若是有多个矩阵变量,应当使用英文苻号; 进行分隔。</li><li> 若是一个矩阵变量有多个值,应当使用英文符号,进行分隔,或之命名多个重复的key即可。</li>
</ol>

ParameterTestController

@RestController
public class ParameterTestController {/* @PathVariable 获取路径中的变量* 获取单个属性 @PathVariable("id") Integer id* 获取全部属性 @PathVariable Map<String, String> pv*/@GetMapping("/car/{id}/owner/{username}")public Map<String, Object> testPathVariable(@PathVariable("id") Integer id,@PathVariable("username") String name,@PathVariable Map<String, String> pv) {Map<String, Object> map = new HashMap<>();map.put("id", id);map.put("name", name);map.put("pv", pv);return map;}/* @RequestHeader 获取浏览器请求头信息* 获取单个请求头信息 @RequestHeader("host") String host* 获取全部请求头信息 @RequestHeader Map<String, String> headers*/@GetMapping("/headers")public Map<String, Object> testRequestHeader(@RequestHeader("host") String host,@RequestHeader Map<String, String> headers) {Map<String, Object> map = new HashMap<>();map.put("单独获取的:host", host);map.put("headers", headers);return map;}/* @RequestParam 获取请求参数* 获取单个请请求参数 @RequestParam("age") Integer age* 获取全部请请求参数 @RequestParam Map<String, String> params*/@GetMapping("/param")public Map<String, Object> testRequestParam(@RequestParam("age") Integer age,@RequestParam("inters") List<String> inters,@RequestParam Map<String, String> params) {Map<String, Object> map = new HashMap<>();map.put("age", age);map.put("inters", inters);map.put("Params", params);return map;}/* 向浏览器响应一个JSESSIONID的Cookie*/@GetMapping("/setcookie")public String testSession(HttpServletRequest request) {// 向浏览器响应一个JSESSIONID的CookieHttpSession session = request.getSession();return "成功";}/* @CookieValue 获取cookie值* 获取指定Cookie值:@CookieValue("JSESSIONID") String jsessionId* 获取指定Cookie对象:@CookieValue("JSESSIONID") Cookie cookie*/@GetMapping("/cookies")public Map<String, Object> testCookieValue(@CookieValue("JSESSIONID") String jsessionId,@CookieValue("JSESSIONID") Cookie cookie) {Map<String, Object> map = new HashMap<>();map.put("JSESSIONID", jsessionId);System.out.println(cookie.getName() + ":" + cookie.getValue());return map;}/* @RequestBody 获取请求体的值* 获取 @RequestBody String content*/@PostMapping("/save")public Map<String, Object> testRequestBody(@RequestParam("userName") String userName,@RequestParam("email") String email,@RequestBody String content) {Map<String, Object> map = new HashMap<>();map.put("userName", userName);map.put("email", email);map.put("content", content);return map;}/* @MatrixVariable 访问路径:/cars/sell;low=34;brand=byd,audi,yd* 获取单个属性:@MatrixVariable("low") Integer low* 获取多个属性:@MatrixVariable("brand") List<String> brand*/@GetMapping("/cars/{path}")public Map<String, Object> testMatrixVariable(@MatrixVariable("low") Integer low,@MatrixVariable("brand") List<String> brand,@PathVariable("path") String path) {Map<String, Object> map = new HashMap<>();map.put("path", path);map.put("low", low);map.put("brand", brand);return map;}/* 矩阵变量中属性名相同*/@GetMapping("/boss/{bossId}/{empId}")public Map<String, Object> testBoos(@MatrixVariable(value = "age", pathVar = "bossId") Integer bossAge,@MatrixVariable(value = "age", pathVar = "empId") Integer empAge,@PathVariable("bossId") Integer bossId,@PathVariable("empId") Integer empId) {Map<String, Object> map = new HashMap<>();map.put("bossAge", bossAge);map.put("empAge", empAge);map.put("bossId", bossId);map.put("empId", empId);return map;}
}

RequestController

@Controller
public class RequestController {/* 向request域存属性*/@GetMapping("/goto")public String goToPage(HttpServletRequest request) {request.setAttribute("msg", "成功");request.setAttribute("code", "200");return "forward:/success";}/* @RequestAttribute 获取request域属性* 获取指定值 @RequestAttribute("msg") String msg*/@ResponseBody@GetMapping("/success")public Map<String, Object> testRequestAttribute(@RequestAttribute("msg") String msg,@RequestAttribute("code") Integer code) {Map<String, Object> map = new HashMap<>();map.put("msg", msg);map.put("code", code);return map;}
}

2.2、Servlet API

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

1、缓存参数解析器

在执行 handler 前,缓存参数解析器

方法参数传入 ServletRequest。

循环26个参数解析器,找到处理Servlet的解析器。

位置:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite

HandlerMethodArgumentResolver 方法

/* Find a registered {@link HandlerMethodArgumentResolver} that supports* the given method parameter.*/
@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、匹配规则

挨个调用参数解析器内部匹配规则,这里查看的是 ServletRequestMethodArgumentResolver 类的匹配规则。

位置:org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver

supportsParameter 方法

// 拿到参数的类型,看是否支持(一下参数类型中的任意一种)
@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);
}

3、参数处理器处理细节

位置:org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver

resolveArgument 方法

// webRequest:将原生的Request和Response包装起来
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {// 拿到传入参数对象类型(控制器方法形参写的是:HttpServletRequest)Class<?> paramType = parameter.getParameterType();// 判断下面对象类型// WebRequest / NativeWebRequest / ServletWebRequestif (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 / MultipartHttpServletRequestif (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {// 进入 resolveNativeRequest 方法return resolveNativeRequest(webRequest, paramType);}// HttpServletRequest required for all further argument typesreturn resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));
}

4、resolveNativeRequest 方法

位置:org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver

resolveNativeRequest 方法

private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {// 拿到原生的Request请求T nativeRequest = webRequest.getNativeRequest(requiredType);if (nativeRequest == null) {throw new IllegalStateException("Current request is not of type [" + requiredType.getName() + "]: " + webRequest);}// 返回原生Request请求return nativeRequest;
}

2.3、复杂参数

Map、Model、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder

1、Map、Model、HttpServletRequest、HttpServletResponse

map、model里面的数据会被放在request的请求域 request.setAttribute。

准备控制器

@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<String, Object> testRequestAttribute(@RequestAttribute(value = "msg",required = false) String msg,@RequestAttribute(value = "code",required = false) Integer code,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;
}

2、RedirectAttributes

3、ServletResponse

2.4、源码解析

1、获取参数解析器

开始循环判断可以执行的参数解析器

位置:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite

HandlerMethodArgumentResolver 方法

/* Find a registered {@link HandlerMethodArgumentResolver} that supports* the given method parameter.*/
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {// 判断到 MapMethodProcessorif (resolver.supportsParameter(parameter)) {result = resolver;// 参数解析器存入缓存// Map参数:MapMethodProcessor// Model参数:ModelMethodProcessor// Request参数:ServletRequestMethodArgumentResolver// Response参数:ServletResponseMethodArgumentResolverthis.argumentResolverCache.put(parameter, result);break;}}}return result;
}

2、MapMethodProcessor

从缓存中获取对应的参数解析器,进入解析器内部方法查看如何解析的。

位置:org.springframework.web.method.annotation.MapMethodProcessor

resolveArgument 方法

@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");// 如果是Map类型的参数,会返回 mavContainer.getModel();---> BindingAwareModelMap();--->是Model也是Mapreturn mavContainer.getModel();
}

进入 getModel 方法

位置:org.springframework.web.method.support.ModelAndViewContainer

/* Return the model to use -- either the "default" or the "redirect" model.* The default model is used if {@code redirectModelScenario=false} or* there is no redirect model (i.e. RedirectAttributes was not declared as* a method argument) and {@code ignoreDefaultModelOnRedirect=false}.*/
public ModelMap getModel() {if (useDefaultModel()) {return this.defaultModel;// private final ModelMap defaultModel = new BindingAwareModelMap();}else {if (this.redirectModel == null) {this.redirectModel = new ModelMap();}return this.redirectModel;}
}

3、ModelMethodProcessor

位置:org.springframework.web.method.annotation.ModelMethodProcessor

resolveArgument 方法

@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");// 调用与Map类型的参数解析器相同方法,会返回 mavContainer.getModel();---> BindingAwareModelMap();内存地址都相同return mavContainer.getModel();
}

所以证明 Map 和 Model 处理都是相同的
10、请求参数处理

4、数据放入请求域中

执行目标方法

10、请求参数处理
目标方法执行完

位置:org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod

invokeAndHandle 方法

最终目标方法的返回值 “forward:/success”

10、请求参数处理
10、请求参数处理

2.5、自定义对象参数

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

bean

/
* 姓名:<input name="userName" value="zhangsan"/> <br/>
* 年龄:<input name="age" value="18"/> <br/>
* 生日:<input name="birth" value="2019/12/10"/> <br/>
* 宠物姓名:<input name="pet.name" value="阿猫"/> <br/>
* 宠物年龄:<input name="pet.age" value="5"/> <br/>
* <input type="submit" value="保存">
*/@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;
}

controller

@RestController
public class ParameterTestController {@PostMapping("/saveuser")public Person saveuser(Person person) {return person;}
}

3、POJO封装过程

数据绑定:页面提交的请求数据(GET、POST)都可以和对象属性进行绑定

自定义类型参数是由 ServletModelAttributeMethodProcessor(参数解析器)解析的

10、请求参数处理

4、参数原理

用户只需要给方法参数位置标记注解,SpringMVC就可以自动在调用目标方法的时候为参数赋值。

分析自动赋值的过程以及源码。

4.1、思路

  • 在 HandlerMapping(处理器映射器)中找到能处理请求的 Handler(处理器)
  • 为当前 Handler 找一个HandlerAdapter (适配器),实际的适配器 RequestMappingHandlerAdapter
  • 适配器执行目标方法并确定方法参数的每一个值,返回结果

4.2、进入 DispatcherServlet 类

这个类是SpringMVC处理请求的入口,doDispatch方法是核心,将断点打带doDispatch方法中。

位置:org.springframework.web.servlet.DispatcherServlet

doDispatch 方法

/* Process the actual dispatching to the handler.* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters* to find the first that supports the handler class.* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers* themselves to decide which methods are acceptable.* @param request current HTTP request* @param response current HTTP response* @throws Exception in case of any kind of processing failure*/
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.// 通过 HandlerMapping(处理器映射器)获取 Handler(处理器:封装了Controller.method的详细信息)mappedHandler = getHandler(processedRequest);

4.3、进入 getHandler 方法

位置:org.springframework.web.servlet.DispatcherServlet

getHandler 方法

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) { // 获取到5个 HandlerMapping(处理器映射器)for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;
}

10、请求参数处理
在RequestMappingHandleMapping注解中映射中心有的映射关系。

10、请求参数处理
继续 doDispatch 方法

位置:org.springframework.web.servlet.DispatcherServlet

			if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.// 获取 HandlerAdapter(处理器适配器:完成控制器方法的调用,参数的传递)HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

4.4、进入 getHandlerAdapter 方法

为当前的 Handler(处理器)找到 HandlerAdapter(处理器适配器)

位置:org.springframework.web.servlet.DispatcherServlet

/* Return the HandlerAdapter for this handler object.* @param handler the handler object to find an adapter for* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.*/
// 传入一个 handler
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {// 循环4种处理器适配器for (HandlerAdapter adapter : this.handlerAdapters) {// 判断适配器能否支持handler,handler被封装成{HandlerMethod@5332}if (adapter.supports(handler)) {

找到4种处理器适配器,判断并使用其中一种。

10、请求参数处理
0 - 支持方法上标注@RequestMapping
1 - 支持函数式编程的
(其它的就不在描述)…

1、进入 supports 方法

位置:org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter

/* This implementation expects the handler to be an {@link HandlerMethod}.* @param handler the handler instance to check* @return whether or not this adapter can adapt the given handler*/
@Override
public final boolean supports(Object handler) {// 判断handler 类型与 HandlerMethod 是否相同 结果:相同return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

2、回到 getHandlerAdapter 方法

位置:org.springframework.web.servlet.DispatcherServlet

// 判断结果相同,返回第一个处理器适配器:{RequestMappingHandlerAdapter@6352}return adapter;}}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

3、回到 doDispatch 方法

位置:org.springframework.web.servlet.DispatcherServlet

			// Process last-modified header, if supported by the handler.// 判断请求是不是GET方法String method = request.getMethod();boolean isGet = "GET".equals(method);// 判断是不是HEADif (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.// HandlerAdapter(处理器适配器) 调用 handle(处理器) 处理目标方法mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

4.5、调用执行目标方法

1、进入 handle 方法

位置:org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter

handle 方法

/* This implementation expects the handler to be an {@link HandlerMethod}.*/
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return handleInternal(request, response, (HandlerMethod) handler);
}

2、接口

位置:org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter

handleInternal 方法

@Nullable
protected abstract ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;

3、进入 handleInternal 方法

位置:org.springframework.web.servlet.mvc.method.annotation.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 necessarymav = invokeHandlerMethod(request, response, handlerMethod);}}else {// No synchronization on session demanded at all...// 执行 handler 的方法mav = invokeHandlerMethod(request, response, handlerMethod);	

4.6、准备执行目标方法

1、进入 invokeHandlerMethod 方法

位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

invokeHandlerMethod 方法

/* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}* if view resolution is required.* @since 4.2* @see #createInvocableHandlerMethod(HandlerMethod)*/
@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 参数解析器集合if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}

2、参数解析器

在目标方法执行前(invocableMethod 可执行目标方法:就是目标方法,又被封装了一次)设置26个参数解析器,作用:确定将要执行的目标方法的每一个参数的值是什么。

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

确定目标方法参数解析器,并将其放入 invocableMethod 中。

10、请求参数处理
参数解析器接口查看

/* Strategy interface for resolving method parameters into argument values in* the context of a given request. @author Arjen Poutsma* @since 3.1* @see HandlerMethodReturnValueHandler*/
public interface HandlerMethodArgumentResolver {/* Whether the given {@linkplain MethodParameter method parameter} is* supported by this resolver.* @param parameter the method parameter to check* @return {@code true} if this resolver supports the supplied parameter;* {@code false} otherwise*/// 当前解析器是否支持解析这种参数boolean supportsParameter(MethodParameter parameter);/* Resolves a method parameter into an argument value from a given request.* A {@link ModelAndViewContainer} provides access to the model for the* request. A {@link WebDataBinderFactory} provides a way to create* a {@link WebDataBinder} instance when needed for data binding and* type conversion purposes.* @param parameter the method parameter to resolve. This parameter must* have previously been passed to {@link #supportsParameter} which must* have returned {@code true}.* @param mavContainer the ModelAndViewContainer for the current request* @param webRequest the current request* @param binderFactory a factory for creating {@link WebDataBinder} instances* @return the resolved argument value, or {@code null} if not resolvable* @throws Exception in case of errors with the preparation of argument values*/// 支持就调用 resolveArgument 方法进行解析@NullableObject resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;}

3、返回值处理器

继续 invokeHandlerMethod 方法

位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

		// 返回值处理器:目标方法可以写多少种类型的返回值if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}

确定目标方法的返回值类型,并将其放入 invocableMethod 中。

10、请求参数处理
10、请求参数处理

4、返回 invocableMethod 类

返回包装后的 invocableMethod (可执行目标方法)

继续 invokeHandlerMethod 方法

位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

		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(目标方法,内部需要准备的全部封装完毕)invocableMethod.invokeAndHandle(webRequest, mavContainer);

4.7、执行目标方法

1、进入 invokeAndHandle 方法

位置:org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod

invokeAndHandle 方法

/* Invoke the method and handle the return value through one of the* configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.* @param webRequest the current request* @param mavContainer the ModelAndViewContainer for this request* @param providedArgs "given" arguments matched by type (not resolved)*/
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 执行当前请求(真正执行目标方法)Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

2、真正执行目标方法

位置:org.springframework.web.method.support.InvocableHandlerMethod

invokeForRequest 方法

/* Invoke the method after resolving its argument values in the context of the given request.* <p>Argument values are commonly resolved through* {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.* The {@code providedArgs} parameter however may supply argument values to be used directly,* i.e. without argument resolution. Examples of provided argument values include a* {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.* Provided argument values are checked before argument resolvers.* <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the* resolved arguments.* @param request the current request* @param mavContainer the ModelAndViewContainer for this request* @param providedArgs "given" arguments matched by type, not resolved* @return the raw value returned by the invoked method* @throws Exception raised if no suitable argument resolver can be found,* or if the method raised an exception* @see #getMethodArgumentValues* @see #doInvoke*/
@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);
}

10、请求参数处理

4.8、获取方法参数值

1、进入 getMethodArgumentValues 方法

位置:org.springframework.web.method.support.InvocableHandlerMethod

getMethodArgumentValues 方法

作用:确定目标方法每一个参数的值。

/* Get the method argument values for the current request, checking the provided* argument values and falling back to the configured argument resolvers.* <p>The resulting array will be passed into {@link #doInvoke}.* @since 5.1.2*/
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 获取方法所有的参数声明(获取方法上所有参数的详细信息)MethodParameter[] parameters = getMethodParameters();// 判断参数不为空if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}// 有参数列表,就按照参数数量创建一个Object数组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)) {

2、supportsParameter

位置:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite

supportsParameter 方法

/* Whether the given {@linkplain MethodParameter method parameter} is* supported by any registered {@link HandlerMethodArgumentResolver}.*/
@Override
public boolean supportsParameter(MethodParameter parameter) {return getArgumentResolver(parameter) != null;
}

位置:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite

getArgumentResolver方法

/* Find a registered {@link HandlerMethodArgumentResolver} that supports* the given method parameter.*/
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {// 遍历26个参数解析器for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {// 挨个判断26个参数解析器谁能解析 (parameter) 参数,通过参数的注解判断if (resolver.supportsParameter(parameter)) {result = resolver;// 将参数解析器放入缓存中,方便后面使用this.argumentResolverCache.put(parameter, result);break;}}}return result;
}

3、回到 getMethodArgumentValues 方法

继续 getMethodArgumentValues 方法

位置:org.springframework.web.method.support.InvocableHandlerMethod

			throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));}try {// 解析这个参数的值args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);

4、进入 resolveArgument 方法

位置:org.springframework.web.method.support.HandlerMethodArgumentResolverComposite

resolveArgument 方法

/* Iterate over registered* {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}* and invoke the one that supports it.* @throws IllegalArgumentException if no suitable argument resolver is found*/
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {// 获取当前参数的参数解析器HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);if (resolver == null) {throw new IllegalArgumentException("Unsupported parameter type [" +parameter.getParameterType().getName() + "]. supportsParameter should be called first.");}// 调用参数解析器的 resolveArgument 方法return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

4.9、参数解析器

1、进入 resolveArgument 方法

位置:org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver

resolveArgument 方法

@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {// 参数的名字NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);MethodParameter nestedParameter = parameter.nestedIfOptional();// 解析名字Object resolvedName = resolveStringValue(namedValueInfo.name);// 名字为空if (resolvedName == null) {throw new IllegalArgumentException("Specified name must not resolve to null: [" + namedValueInfo.name + "]");}// 解析值Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);

2、进入 resolveName 方法

位置:org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver

resolveName 方法

@Override
@SuppressWarnings("unchecked")
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {// 从request域中获取map集合,map集合内存储的是路径变量Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);// 当map集合不为空,根据key返回valuereturn (uriTemplateVars != null ? uriTemplateVars.get(name) : null);
}

属性来源?

请求一进来,UrlPathHelper 先把url地址中路径变量解析出来,并保存到 Request 请求域中,这个参数解析器,直接获取请求域中的值,但是请求域中封装了所有路径变量的值,需要解析获取id的值3。

10、请求参数处理计算id值
10、请求参数处理
继续 resolveArgument 方法

位置:org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver

	if (arg == null) { // age:"3"if (namedValueInfo.defaultValue != null) {arg = resolveStringValue(namedValueInfo.defaultValue);}else if (namedValueInfo.required && !nestedParameter.isOptional()) {handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);}arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());}else if ("".equals(arg) && namedValueInfo.defaultValue != null) {arg = resolveStringValue(namedValueInfo.defaultValue);}if (binderFactory != null) {WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);try {arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);}catch (ConversionNotSupportedException ex) {throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),namedValueInfo.name, parameter, ex.getCause());}catch (TypeMismatchException ex) {throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),namedValueInfo.name, parameter, ex.getCause());}}handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);return arg;
}

3、循环解析所有参数

继续 getMethodArgumentValues 方法

位置:位置:org.springframework.web.method.support.InvocableHandlerMethod

第一个参数解析出来,继续循环解析下一个参数,知道全部解析完毕。

10、请求参数处理

4、getMethodArgumentValues 方法结束

继续getMethodArgumentValues 方法

位置:org.springframework.web.method.support.InvocableHandlerMethod

		}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;
}

4.10、自定义类型参数 POJO

自定义类型参数是由 ServletModelAttributeMethodProcessor(参数解析器)解析的

位置:org.springframework.beans.BeanUtils

isSimpleValueType 方法

/* Check if the given type represents a "simple" value type: a primitive or* primitive wrapper, an enum, a String or other CharSequence, a Number, a* Date, a Temporal, a URI, a URL, a Locale, or a Class.* <p>{@code Void} and {@code void} are not considered simple value types.* @param type the type to check* @return whether the given type represents a "simple" value type* @see #isSimpleProperty(Class)*/
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 方法

/* Resolve the argument from the model or if not found instantiate it with* its default if it is available. The model attribute is then populated* with request values via data binding and optionally validated* if {@code @java.validation.Valid} is present on the argument.* @throws BindException if data binding and validation result in an error* and the next method parameter is not of type {@link Errors}* @throws Exception if WebDataBinder initialization fails*/
@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 instancetry {// 创建一个实例(空实体类对象对象)attribute = createAttribute(name, parameter, binderFactory, webRequest);}catch (BindException ex) {if (isBindExceptionRequired(parameter)) {// No BindingResult parameter -> fail with BindExceptionthrow ex;}// Otherwise, expose null/empty value and associated BindingResultif (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.// 利用 binderFactory.createBinder 创建一个 WebDataBinder(web数据绑定器) WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);// 拿到 getTarget() 对象,并且不为空if (binder.getTarget() != null) {if (!mavContainer.isBindingDisabled(name)) {// 将原生请求中的数据与绑定器中空实体类绑定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.Optionalif (!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 modelMap<String, Object> bindingResultModel = bindingResult.getModel();mavContainer.removeAttributes(bindingResultModel);mavContainer.addAllAttributes(bindingResultModel);return attribute;
}

WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
WebDataBinder :web数据绑定器,将请求参数的值绑定到指定的JavaBean里面
WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中

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

@FunctionalInterfacepublic interface Converter<S, T>

定制Converter

未来我们可以给WebDataBinder里面放自己的Converter;
private static final class StringToNumber implements Converter<String, T>

4.11、域对象共享

1、回到 invokeAndHandle 方法

位置:org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod

Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// mavContainer:利用map或model添加这两个值

10、请求参数处理

	// 设置响应状态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;}
}

2、处理返回值

位置:org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite

handleReturnValue 方法

/* Iterate over registered {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers} and invoke the one that supports it.* @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.*/
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}// 进入 handleReturnValue 方法handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

3、进入 handleReturnValue 方法

位置:org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler

handleReturnValue 方法

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {// 如果返回值是字符串if (returnValue instanceof CharSequence) {// 拿到返回的字符串String viewName = returnValue.toString();// 保存到 mavContainer(模型和视图容器)mavContainer.setViewName(viewName);if (isRedirectViewName(viewName)) {mavContainer.setRedirectModelScenario(true);}}else if (returnValue != null) {// should not happenthrow new UnsupportedOperationException("Unexpected return type: " +returnType.getParameterType().getName() + " in method: " + returnType.getMethod());}
}

4.12、继续执行

将所有的数据都放在 ModelAndViewContainer,包含要去页面地址View和Model数据。

1、回到 invokeHandlerMethod方法

位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

		if (asyncManager.isConcurrentHandlingStarted()) {return null;}// 返回值处理完之后,返回 getModelAndView(获取ModelAndView对象)return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}
}

2、进入 getModelAndView 方法

位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

getModelAndView 方法

@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {// 模型工厂modelFactory.updateModel(webRequest, mavContainer);if (mavContainer.isRequestHandled()) {return null;}// 拿到ModelModelMap model = mavContainer.getModel();// 封装成 ModelAndViewModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());if (!mavContainer.isViewReference()) {mav.setView((View) mavContainer.getView());}// model 如果是 RedirectAttributes(重定向携带数据)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;
}

3、进入 updateModel 方法

位置:org.springframework.web.method.annotation.ModelFactory

updateModel 方法

/* Promote model attributes listed as {@code @SessionAttributes} to the session.* Add {@link BindingResult} attributes where necessary.* @param request the current request* @param container contains the model to update* @throws Exception if creating BindingResult attributes fails*/
public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception {// ModelAndViewContainer 中拿到默认的 modelModelMap defaultModel = container.getDefaultModel();if (container.getSessionStatus().isComplete()){this.sessionAttributesHandler.cleanupAttributes(request);}else {this.sessionAttributesHandler.storeAttributes(request, defaultModel);}if (!container.isRequestHandled() && container.getModel() == defaultModel) {// 更新最终的绑定结果updateBindingResult(request, defaultModel);}
}

4、进入 updateBindingResult 方法

位置:

updateBindingResult 方法

/* Add {@link BindingResult} attributes to the model for attributes that require it.*/
private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {// 拿到所有Model中的keyList<String> keyNames = new ArrayList<>(model.keySet());for (String name : keyNames) {Object value = model.get(name);if (value != null && isBindingCandidate(name, value)) {String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;if (!model.containsAttribute(bindingResultKey)) {WebDataBinder dataBinder = this.dataBinderFactory.createBinder(request, value, name);model.put(bindingResultKey, dataBinder.getBindingResult());}}}
}

5、回到 handleInternal 方法

位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

	}// 方法执行之后if (!response.containsHeader(HEADER_CACHE_CONTROL)) {if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);}else {prepareResponse(response);}}return mav;
}

6、回到 doDispatch 方法

位置:org.springframework.web.servlet.DispatcherServlet

			// 在执行handle之后,返回mvif (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 afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}
}

7、进入 processDispatchResult 方法

位置:org.springframework.web.servlet.DispatcherServlet

processDispatchResult 方法

/* Handle the result of handler selection and handler invocation, which is* either a ModelAndView or an Exception to be resolved to a ModelAndView.*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}// Did the handler return a view to render?// mv:ModelAndViewif (mv != null && !mv.wasCleared()) {// 渲染页面render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isTraceEnabled()) {logger.trace("No view rendering, null ModelAndView returned.");}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Concurrent handling started during a forwardreturn;}if (mappedHandler != null) {// Exception (if any) is already handled..mappedHandler.triggerAfterCompletion(request, response, null);}
}

位置:org.springframework.web.servlet.DispatcherServlet

render 方法

/* Render the given ModelAndView.* <p>This is the last stage in handling a request. It may involve resolving the view by name.* @param mv the ModelAndView to render* @param request current HTTP servlet request* @param response current HTTP servlet response* @throws ServletException if view is missing or cannot be resolved* @throws Exception if there's a problem rendering the view*/
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {// Determine locale for request and apply it to the response.Locale locale =(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());response.setLocale(locale);View view;// 获取视图名String viewName = mv.getViewName();if (viewName != null) {// We need to resolve the view name.// 解析视图view = resolveViewName(viewName, mv.getModelInternal(), locale, request);

8、解析视图

位置:org.springframework.web.servlet.DispatcherServlet

resolveViewName 方法

/* Resolve the given view name into a View object (to be rendered).* <p>The default implementations asks all ViewResolvers of this dispatcher.* Can be overridden for custom resolution strategies, potentially based on* specific model attributes or request parameters.* @param viewName the name of the view to resolve* @param model the model to be passed to the view* @param locale the current locale* @param request current HTTP servlet request* @return the View object, or {@code null} if none found* @throws Exception if the view cannot be resolved* (typically in case of problems creating an actual View object)* @see ViewResolver#resolveViewName*/
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,Locale locale, HttpServletRequest request) throws Exception {if (this.viewResolvers != null) {for (ViewResolver viewResolver : this.viewResolvers) {// 获取视图View view = viewResolver.resolveViewName(viewName, locale);if (view != null) {return view;}}}return null;
}

位置:org.springframework.web.servlet.view.ContentNegotiatingViewResolver

resolveViewName 方法

@Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {// 拿到所有请求域中的属性RequestAttributes attrs = RequestContextHolder.getRequestAttributes();Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());if (requestedMediaTypes != null) {List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);// 获取到所有的视图View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);if (bestView != null) {return bestView;}}String mediaTypeInfo = logger.isDebugEnabled() && requestedMediaTypes != null ?" given " + requestedMediaTypes.toString() : "";if (this.useNotAcceptableStatusCode) {if (logger.isDebugEnabled()) {logger.debug("Using 406 NOT_ACCEPTABLE" + mediaTypeInfo);}return NOT_ACCEPTABLE_VIEW;}else {logger.debug("View remains unresolved" + mediaTypeInfo);return null;}
}

回到render 方法

位置:org.springframework.web.servlet.DispatcherServlet

		if (view == null) {throw new ServletException("Could not resolve view with name '" + mv.getViewName() +"' in servlet with name '" + getServletName() + "'");}}else {// No need to lookup: the ModelAndView object contains the actual View object.view = mv.getView();if (view == null) {throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +"View object in servlet with name '" + getServletName() + "'");}}// Delegate to the View object for rendering.if (logger.isTraceEnabled()) {logger.trace("Rendering view [" + view + "] ");}try {if (mv.getStatus() != null) {response.setStatus(mv.getStatus().value());}// 页面渲染数据view.render(mv.getModelInternal(), request, response);}catch (Exception ex) {if (logger.isDebugEnabled()) {logger.debug("Error rendering view [" + view + "]", ex);}throw ex;}
}

位置:org.springframework.web.servlet.view.AbstractView

render 方法

/* Prepares the view given the specified model, merging it with static* attributes and a RequestContext attribute, if necessary.* Delegates to renderMergedOutputModel for the actual rendering.* @see #renderMergedOutputModel*/
@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,HttpServletResponse response) throws Exception {if (logger.isDebugEnabled()) {logger.debug("View " + formatViewName() +", model " + (model != null ? model : Collections.emptyMap()) +(this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));}// 创建合并输出模型Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);

位置:org.springframework.web.servlet.view.AbstractView

createMergedOutputModel 方法

/* Creates a combined output Map (never {@code null}) that includes dynamic values and static attributes.* Dynamic values take precedence over static attributes.*/
protected Map<String, Object> createMergedOutputModel(@Nullable Map<String, ?> model,HttpServletRequest request, HttpServletResponse response) {@SuppressWarnings("unchecked")Map<String, Object> pathVars = (this.exposePathVariables ?(Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null);// Consolidate static and dynamic model attributes.int size = this.staticAttributes.size();size += (model != null ? model.size() : 0);size += (pathVars != null ? pathVars.size() : 0);Map<String, Object> mergedModel = new LinkedHashMap<>(size);mergedModel.putAll(this.staticAttributes);if (pathVars != null) {mergedModel.putAll(pathVars);}// 如果model不等于nullif (model != null) {// 将model中数据放入整合模型mergedModel.putAll(model);}// Expose RequestContext?if (this.requestContextAttribute != null) {mergedModel.put(this.requestContextAttribute, createRequestContext(request, response, mergedModel));}return mergedModel;
}

继续 render 方法

位置:org.springframework.web.servlet.view.AbstractView

	// 准备响应prepareResponse(request, response);// 渲染合并输出的模型数据renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

进入 renderMergedOutputModel 方法

位置:org.springframework.web.servlet.view.InternalResourceView

@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {// Expose the model object as request attributes.// 公开model作为请求域属性,将model中的数据遍历存入request域中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);}
}