本篇内容与参数解析原理、域对象数据共享 有较多重复,就算是复习吧,夯实基础。
首先,新建spring项目:demo4。
然后,java目录下创建控制器类Demo4Controller。
package com.example.boot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Controller
public class Demo4Controller {
@GetMapping("/params")
public String getParams(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("message2","helloworld2");
response.addCookie(cookie);
return "forward:/success";
}
@GetMapping("/success")
@ResponseBody
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;
}
}
注意哈:Demo4Controller类上的注解是@Controller
,不是@RestController
。
Demo4Controller#success()方法上的注解有@ResponseBody
。
并在resources.static目录下新建静态页index.html。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<a href="/params">getParams</a>
</body>
</html>
启动应用,进行调试,具体调试过程如下。
- MapMethodProcessor#resolveArgument
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();
}
- ModelAndViewContainer
public class ModelAndViewContainer {
//...
private final ModelMap defaultModel = new BindingAwareModelMap();
//...
public ModelMap getModel() {
if (this.useDefaultModel()) {
return this.defaultModel;
} else {
if (this.redirectModel == null) {
this.redirectModel = new ModelMap();
}
return this.redirectModel;
}
}
//...
}
//BindingAwareModelMap
public class BindingAwareModelMap extends ExtendedModelMap {}
//ExtendedModelMap
public class ExtendedModelMap extends ModelMap implements Model {}
//ModelMap
public class ModelMap extends LinkedHashMap<String, Object> {}
//LinkedHashMap
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>{}
//HashMap
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {}
//Map
public interface Map<K,V> {}
//Model
public interface Model {}
解析请求参数:Map和Model时,调用MapMethodProcessor#resolveArgument(),将返回BindingAwareModelMap类型实例,且为同一个实例。
另外,从继承层次来看,BindingAwareModelMap类既为Map,也为Model。
Map或Model,可实现将数据放到request域中,相当于request.setAttribute。不妨调试一下,如下。
- org.springframework.web.servlet.DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//...
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
//...
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//...
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
//...
}
- org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return this.handleInternal(request, response, (HandlerMethod)handler);
}
- org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//...
mav = this.invokeHandlerMethod(request, response, handlerMethod);
//...
return mav;
}
- org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//...
invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
//...
var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
//...
return var15;
}
- org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return this.doInvoke(args);
}
- org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//...
Object[] args = new Object[parameters.length];
for(int i = 0; i < parameters.length; ++i) {
//...
if (args[i] == null) {
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
} catch (Exception var10) {
//...
}
}
}
return args;
}
- org.springframework.web.method.annotation.MapMethodProcessor#resolveArgument
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();
}
- org.springframework.web.servlet.DispatcherServlet#processDispatchResult
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
//...
this.render(mv, request, response);
//...
}
- org.springframework.web.servlet.DispatcherServlet#render
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
//...
view.render(mv.getModelInternal(), request, response);
//...
}
- org.springframework.web.servlet.view.AbstractView#render
public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//...
this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);
}
- org.springframework.web.servlet.view.InternalResourceView#renderMergedOutputModel
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
this.exposeModelAsRequestAttributes(model, request);
//...
}
- org.springframework.web.servlet.view.AbstractView#exposeModelAsRequestAttributes
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);
}
});
}
如上,Map或者Model中的数据,最终在AbstractView#exposeModelAsRequestAttributes方法中通过request.setAttribute()
保存在request域中。