分析如何将map/model的数据放入request保存域
- 以下源码不需要全看,主要看写了中文注释的地方即可,一步一步进去就明白了
/*首先是进入到DispatcherServlet这个中央控制器中的doDispatcher()方法里的这一步(每个请求都会经由
DispatcherServlet处理,进入到doDispatcher()方法)*/
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//=========进入ha.handle
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
//================进入handleInternal
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;
}
//================进入invokeHandlerMethod(request, response, handlerMethod);
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);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
//这里会创建mavContainer
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();
}
}
//===================进入invocableMethod.invokeAndHandle(webRequest, mavContainer);
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//invokeFOrRequest,进入的就是这里
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;
}
}
//=================进入invokeForRequest(webRequest, mavContainer, providedArgs)
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//进入这里面会返回args数组,这个args数组保存的就是我们需要的每个参数解析后的值/对象
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
//!!!!执行我们的控制器方法,底层是一系列反射
return doInvoke(args);
}
//===================进入getMethodArgumentValues(request, mavContainer, providedArgs)
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
//args数组保存的就是每个参数的值
Object[] args = new Object[parameters.length];
//这个for循环一次对每个参数进行赋值,存放在args数组中
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)) {
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;
}
//===================进入args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory)
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.");
}
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
//================分两步,先进入getArgumentResolver(parameter)找到当前参数对应的参数解析器
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
//从缓存中直接获取这个参数对应的解析器,第一次的话肯定没有
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
//从所有的argumentResolvers里面一个一个找,找到了就放入缓存里,这个缓存将参数作为键,解析器作为值下一调用直接获取
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
//判断此解析器是否支持当前参数,例如参数是否包含某注解,因此对应某解析器,对象参数也有对应的解析器
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
//================第二步:再进入resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory)
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");
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!关键,这里可以看见我们返回的是mavContainer对象的defaultModel属性
//无论参数是map还是model还是modelmap,他们的解析器虽然不同,但是resolveArgument()方法都是返回mavContainer.getModel()
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
return mavContainer.getModel();
}
//=================进入mavContainer.getModel()
public ModelMap getModel() {
//这里可以看见,如果使用的是默认的模型的话,返回的就是mavContainer这个对象的defaultModel属性
if (useDefaultModel()) {
return this.defaultModel;
}
else {
if (this.redirectModel == null) {
this.redirectModel = new ModelMap();
}
return this.redirectModel;
}
}
-
由上可知,我们参数中
map/model
最终得到的都是mavContainer
这个对象的defaultModel
属性,这个属性的类型是BindingAwareModelMap
,之前讲过这个BindingAwareModelMap
实现了map/model
这两个接口,因此返回这个类型我们的map或者model都可以接收。-
而且如果我们参数同时有
map
和model
,返回的对象实际上是同一个,以为上面可以看到实际上就是调用mavContainer.getModel()
返回的,而mavContainer
这个对象的创建是在解析参数之前,一次请求映射只会创建一个,因此mavContainer.getModel()
返回的是同一个对象。 -
此时我们使用
map
或者model
添加键值对实际上都会动态绑定到BindingAwareModelMap
这个类型调用它的添加方法,因此我们map/model添加的键值对都放在同一个对象里,而这个对象实际上就是mavContainer
的defualtModel
属性,因此我们添加过后这个属性就有了对应的数据。 -
例如这样添加
map.put("1",123); model.addAttribute("2",123);
添加过后
- 可以看到
defalutModel
这个属性有了对应的键值对数据
- 可以看到
-
-
然后执行完了控制器里面的方法后返回到
invocableMethod.invokeAndHandle(webRequest, mavContainer)
这一步protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); try { //...省略很多 //这里创建的mavContainer对象 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); //...省略很多 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(); } } //===================进入getModelAndView(mavContainer, modelFactory, webRequest)方法 private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) { return null; } //得到我们操作更新过的模型,如果不是重定向请求就是default这个 ModelMap model = mavContainer.getModel(); //将这个mavContainer映射的视图名称和模型数据都作为参数new一个ModelAndView对象出来,这个就是我们最后得到的,这里返回它 //!!!!!!!!!!!!!!!!!!!!!!! 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; } //之后就是将这个创建出来的mav层层返回直到mv = ha.handle(processedRequest, response, mappedHandler.getHandler())这里
-
至此,我们已经将我们控制器方法里面的键值对添加到了doDispatcher方法里面的mav对象中,之后真正将这个mav对象中的模型数据放到request保存域中的方法是
mv = ha.handle(processedRequest, response, mappedHandler.getHandler())
之后的processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)
这个方法进入
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)
方法:private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { //...省略一些不相关的 // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { //render方法渲染视图,进入这里!!!!!!!!!!!!!!!!!!! render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } //后面的省略 } //========================进入render(mv, request, response); protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception { //...省略一些不相关的 try { if (mv.getStatus() != null) { request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, mv.getStatus()); response.setStatus(mv.getStatus().value()); } //继续进入这个render方法!!!!!! view.render(mv.getModelInternal(), request, response); } //后面的省略 } //======================进入view.render(mv.getModelInternal(), request, response); 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); prepareResponse(request, response); //这里是重点,进入这个方法!!!!!!!!!!!!!!!!! renderMergedOutputModel(mergedModel, getRequestToExpose(request), response); } //=====================进入renderMergedOutputModel(mergedModel, getRequestToExpose(request), response) protected void renderMergedOutputModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // Expose the model object as request attributes. //这里就是暴露mdoel的数据作为RequestAttributes,进去!!!!!!!!!!!!! exposeModelAsRequestAttributes(model, request); //后面的省略 } //====================进去exposeModelAsRequestAttributes(model, request); protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception { //可以看到就是遍历model的每一个键值对,将它用request对象的setAttribute()方法设置 model.forEach((name, value) -> { if (value != null) { request.setAttribute(name, value); } else { request.removeAttribute(name); } }); }
-
至此分析完毕。