总结:
找到解析器后,用解析器去解析参数,都使用了resolveArgument()中以下方法
mavContainer.getModel();
mavContainer:模型和视图容器
视图:页面请求要返回一个地方,这个地方的地址叫视图 。比如要到/sucess
模型:携带的数据称为模型
复杂参数
Map Model :会被放在request请求域中,相当于调用了request.setAttribute
Errors/BindingRequest
RedirectAttributes 重定向携带数据
ServletResponse
SessionStatus UriComponentBuilder
ServletComponentBuilder
转发生效前提
return "forward:/success2";
该类必须标注 @Controller 而不是@RestController,标准后者只会输出原文
方法注解参数设定非必须
value="msg2",required = false
@ResponseBody @GetMapping("/success2") public Map success2(@RequestAttribute(value="msg2",required = false) String msg2, @RequestAttribute(value="code",required = false) Integer code, HttpServletRequest request ){
给Request域中放数据 map model request response
TestFuza(Map<String,Object> map,
Model model,
HttpServletRequest request,
HttpServletResponse response
)
@Controller
public class FuzaController {
@GetMapping("/fuza")
public String TestFuza(Map<String,Object> map,
Model model,
HttpServletRequest request,
HttpServletResponse response
) {
map.put("hello","1234567");
model.addAttribute("city","苏州");
request.setAttribute("province","广东");
Cookie cookie=new Cookie("cookie1","shanghai");
cookie.setDomain("localhost");
response.addCookie(cookie);
return "forward:/success2";
}
@ResponseBody
@GetMapping("/success2")
public Map success2(@RequestAttribute(value="msg2",required = false) String msg2,
@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 city=request.getAttribute("city");
Object province=request.getAttribute("province");
map.put("请求注解",msg1);
map.put("annotion注解2",msg2);
map.put("hello",hello);
map.put("city",city);
map.put("province",province);
return map;
}
}
原理分析1-到获取所有4个请求参数值
Cookie cookie=new Cookie("cookie1","shanghai");设置断点,请求后
我们企图给浏览器设置cookie
浏览器中就会保存这个cookie
进入断点
一路到
stepinto
stepinto
循环27个解析器
可以看到parameter里面包含请求的4个参数: Map,Model,
也就是方法张的参数
public String TestFuza(Map<String,Object> map, Model model, HttpServletRequest request, HttpServletResponse response )
可以看到当前Receiver,进入下一循环
因为第一个参数是map,所以一直到 MapMethodProcessor
进入,会查看当前请求参数是否为Map
放行到去解析
进入
放行到
进入
其中有mavContainer,全称 ModelAndViewContainer去getModel
ctrl进入ModelAndViewContainer,看到defaultModel
private final ModelMap defaultModel = new BindingAwareModelMap();
下面还有ModelMap getModel(),返回 defaultModel
也可以在本类中ctl+f12查看方法
其中有一句
private final ModelMap defaultModel = new BindingAwareModelMap();
ctrl进入类 BindingAwareModelMap
ctrl进入父类 ExtendedModelMap
public class ExtendedModelMap extends ModelMap implements Model {
可以看出ExtendedModelMap 既是ModelMap 又是Model
一路返回
,这样就获取了第一个参数
第二个参数解析
第二个参数编号1,就是model
stepinto
stepinto
stepinto进入遍历Resolvers
一直循环到ModelMethodParameter
stepinto,可以看到参数类型是Model即可支持
继续放行
stepinto
在如下位置stepinto
可以看到调的是与解析参数0一样的方法 mavContainer.getModel();
放行到
发现已经获得的两个参数类型都是@7059,代表他们是同一个对象
放行到获取全部参数
-------------------------------以上获取了全部4个参数----------------------------------------------------
原理分析2-
放行到
可以在controller里面打一个断点
这样放行几步就会先来到上面controller文件
继续放行几步
放行到发现returnValue已经获取 是 forward:/success2
查看其mavContainer参数得到的default model
如何将上文defaultmodel中的两个值放到请求域中
在如下位置this.setResponseStatus(webRequest);进入
进入之后
放行到this.returnValueHandlers.handleReturnValue
其传入参数中包含了 mavContainer
,这是处理返回结果,controller里面返回结果是个string),
stepinto this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest); 获取返回结果类型
放行返回
再次 step into,进入handleReturnValue 方法,找到返回值处理器selectHandler
放行到本方法下面一句
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
看到这个
可以看出把返回值变成字符串放到了mavContainer
视图是要求的网址,模型是携带的数据
returnValue是forward:/success2
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof CharSequence) {
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
if (this.isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
现在mavContainer包含了 view和 default model
继续放行,辛苦啊,终于找到 getModelAndView这个方法所在位置
进入,注意这一句modelFactory.updateModel(webRequest, mavContainer);
从此之后都是modelFactory执行 更新Model,可以进入研究
继续放行,回到DispatcherServlet
放行到mappedHandler.applyPostHandle(processedRequest, response, mv);
它是mv = ha.handle()获取完参数并放到mavcontainer中之后怎么办的方法,进入
放行到
一路放行到
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
Step3 处理派发结果,就是要去哪些页面
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
stepinto
继续放行到,注意这里mv的值,mv是对model and view的再次封装
ModelAndView [view="forward:/success2"; model={hello=1234567, city=苏州}]
放行
进入render()
其中有拿到视图名 String viewName = mv.getViewName();
放行到
对 mv.getModelInternal() 评估一下
Stepinto view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);
来到了视图解析器流程,了解即可
继续放行进入 view.render(mv.getModelInternal(), request, response);
渲染要去的页面
stepinto
放行到核心 创建要去的页面 createMergedOutputModel
Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);
stepinto
放行到
mergedModel.putAll(this.staticAttributes);此时创建了mergedModel
放行到,如果model不为空,那么就都放入 mergedModel
继续放行,返回mergedModel
放行,准备响应repareResponse
放行,渲染合并输出的模型数据
this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);
stepinto getRequestToExpose拿到请求对象
放行回到
再次step into renderMergedOutputModel,
其中有一句暴露model作为请求域的属性,属于类InternalResourceView
this.exposeModelAsRequestAttributes(model, request);
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
this.exposeModelAsRequestAttributes(model, request);
this.exposeHelpers(request);
String dispatcherPath = this.prepareForRendering(request, response);
RequestDispatcher rd = this.getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + this.getUrl() + "]: Check that the corresponding file exists within your web application archive!");
} else {
if (this.useInclude(request, response)) {
response.setContentType(this.getContentType());
if (this.logger.isDebugEnabled()) {
this.logger.debug("Including [" + this.getUrl() + "]");
}
rd.include(request, response);
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Forwarding to [" + this.getUrl() + "]");
}
rd.forward(request, response);
}
}
}
以下方法循环遍历model,赋值给request
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
model.forEach((name, value) -> {
if (value != null) {
request.setAttribute(name, value);
} else {
request.removeAttribute(name);
}
});
}