SpringMvc 源码分析 (返回值如何返回为ModelAndView以及View解析的原理) (十二)

网友投稿 748 2022-11-14

SpringMvc 源码分析 (返回值如何返回为ModelAndView以及View解析的原理) (十二)

SpringMvc 源码分析 (返回值如何返回为ModelAndView以及View解析的原理) (十二)

文章目录

​​0.前记​​​​1.如何封装为ModelAndView​​​​2.视图解析原理​​​​3.视图解析的过程​​

0.前记

根据上文中的返回值解析器会根据不同的返回值解析器去解析成不同的返回值, 但是最后返回值会封装为ModelAndView。本篇的内容就是如何去封装为ModelAndView, 以及如何根据ModelAndView转为View, 最后渲染为真正的视图。

1.如何封装为ModelAndView

DispatcherServlet.doDispatcher -> ha.handler() -> RequestMappingHandlerAdapter.handleInternal()

​​RequestMappingHandlerAdapter.handleInternal()​​

​​RequestMappingHandlerAdapter.invokeHandlerMethod()​​

最后返回值得到ModelAndView:

​​RequestMappingHandlerAdapter.getModelAndView()​​

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) { return null; } ModelMap model = mavContainer.getModel(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); if (request != null) { RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } } return mav; }

ModelFactory 准备模型数据, 请求域数据的共享, session中的数据转到request域中拿到Model 和 View 封装为ModelAndViewContainer判断是否为重定向视图, 如果是的话需要去FlashMap中提取数据

public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception { ModelMap 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); } }

​​applyDefaultViewName​​

private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception { if (mv != null && !mv.hasView()) { String defaultViewName = getDefaultViewName(request); if (defaultViewName != null) { mv.setViewName(defaultViewName); } } } protected String getDefaultViewName(HttpServletRequest request) throws Exception { return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null); } public String getViewName(HttpServletRequest request) { String path = ServletRequestPathUtils.getCachedPathValue(request); return (this.prefix + transformPath(path) + this.suffix); } public static String getCachedPathValue(ServletRequest request) { Object path = getCachedPath(request); if (path instanceof PathContainer) { String value = ((PathContainer) path).value(); path = UrlPathHelper.defaultInstance.removeSemicolonContent(value); } return (String) path; } public static Object getCachedPath(ServletRequest request) { // The RequestPath is pre-parsed if any HandlerMapping uses PathPatterns. // The lookupPath is re-resolved or cleared per HandlerMapping. // So check for lookupPath first. String lookupPath = (String) request.getAttribute(UrlPathHelper.PATH_ATTRIBUTE); if (lookupPath != null) { return lookupPath; } RequestPath requestPath = (RequestPath) request.getAttribute(PATH_ATTRIBUTE); if (requestPath != null) { return requestPath.pathWithinApplication(); } throw new IllegalArgumentException( "Neither a pre-parsed RequestPath nor a pre-resolved String lookupPath is available."); }

如果返回的mv没有视图的话, 会默认给一个视图的, 是根据​​RequestToViewNameTranslator​​组件的功能获取的

从request域中获取到

2.视图解析原理

Controller的返回值返回逻辑视图的路径 -> 通过视图解析器​​ViewResolver​​​形成真正的视图View -> ​​render()​​渲染为视觉效果的视图。

3.视图解析的过程

​​DispatcherServlet.doDispatcher().processDispatchResult()​​

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? if (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 forward return; } if (mappedHandler != null) { // Exception (if any) is already handled.. mappedHandler.triggerAfterCompletion(request, response, null); } }

这个方法中有出现异常的情况和没有异常的情况这里我们先分析没有异常的情况

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); 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) { request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, mv.getStatus()); 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; } }

获取国际化信息, 从国际化信息中得出哪个国家的语言从​​ModelAndView​​​中获取视图名称, 如果视图名称不为空的话,​​resolveViewName​​​解析得到视图, 如果视图名称为空的话, 通过​​ModelAndView​​获取视图最后得到的视图, 通过​​render()​​ 方法到视觉效果的视图。

​​resolveViewName​​

protected View resolveViewName(String viewName, @Nullable Map 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; }

这个方法是通过视图解析器将视图名称​​ViewName​​作为参数得到视图​​View​​的

public View resolveViewName(String viewName, Locale locale) throws Exception { if (!isCache()) { return createView(viewName, locale); } else { Object cacheKey = getCacheKey(viewName, locale); View view = this.viewAccessCache.get(cacheKey); if (view == null) { synchronized (this.viewCreationCache) { view = this.viewCreationCache.get(cacheKey); if (view == null) { // Ask the subclass to create the View object. view = createView(viewName, locale); if (view == null && this.cacheUnresolved) { view = UNRESOLVED_VIEW; } if (view != null && this.cacheFilter.filter(view, viewName, locale)) { this.viewAccessCache.put(cacheKey, view); this.viewCreationCache.put(cacheKey, view); } } } } else { if (logger.isTraceEnabled()) { logger.trace(formatKey(cacheKey) + "served from cache"); } } return (view != UNRESOLVED_VIEW ? view : null); } }

判断是否是​​redirect​​​的视图, 创建​​RedirectView​​如果是​​forward​​​的视图, 创建​​InternalResourceView​​如果是其他情况的话, 创建​​InternalResourceView​​

如果是RedirectView的话

先将数据存放在session中, 使得重定向到下一个位置也可以得到数据response.sendRedirect

如果是InternalResourceView的话

暴露model域的数据到request域中暴露帮助得到request的路径通过request.getRequestDispatcher().forward()转发操作

其他情况

也是定义了一个InternalResourceView视图

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:基于SpringMVC实现网页登录拦截
下一篇:【TWVRP】基于matlab鲸鱼算法求解带时间窗开放式车辆路径问题【含Matlab源码 1986期】
相关文章

 发表评论

暂时没有评论,来抢沙发吧~