总结:
ViewResolver 如果要改需要自己注入到容器中并进行修改, springmvc使用的是InterResourceViewResover
view不需要自己改,是springmvc根据return返回值选的
既然看到有ModelAndView直接跳转jsp的, 有请求转发的,有重定向的,这里整体是怎么设计的: (@ResponseBody的在此不作展开)
HiController:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @Controller public class HiController { @RequestMapping ( "/hi" ) public ModelAndView getHi() { ModelAndView mav = new ModelAndView( "me" ); return mav; } @RequestMapping ( "/yes" ) public String forwardYes() { return "forward:patch" ; } @RequestMapping ( "/no" ) public String RedirectNo() { return "redirect:patch" ; } @ResponseBody @RequestMapping ( "/patch" ) public String redirectNo() { return "from forward or redirect request" ; // 这种情况没有view,在这里不讨论 } } |
主要代码:
DispatcherServlet.doDispatch()里的:
DispatcherServlet.render方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 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); // 1 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); // 2 } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug( "Error rendering view [" + view + "]" , ex); } throw ex; } } |
1. view = resolveViewName()会根据不同的路径生成不同的view, return mav 会返回JstlView, return "forward:/patch" 会返回InternalResourceView, return "direct:/patch" 会返回IndirectView
2. 不同的view去走不同的view.render(), 根据不同的view重写abstract void renderMergedOutputModel方法
再来看是如何生成不同的view:[/code][code]view = resolveViewName() 进去,走到
DiapatcherServlet先有ViewResolver这个,用来生成不同的view
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 ; } |
ViewResolver 接口只有一个方法
1 2 3 4 5 | public interface ViewResolver { @Nullable View resolveViewName(String viewName, Locale locale) throws Exception; } |
要配置具体的视图解析器,springMVC中使用的是InterResourceViewResover,InterResourceViewResover 和他的父类UrlBasedViewResolver中都没有重写resolveViewName方法,再上一层的父类AbstractCahingViewResolver实现了resolveViewName方法
AbstractCahingViewResolver:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | @Override @Nullable 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 ); } } |
InterResourceViewResover中没有createView方法,所以是调用它父类UrlBasedViewResolver的createView方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | @Override protected View createView(String viewName, Locale locale) throws Exception { // If this resolver is not supposed to handle the given view, // return null to pass on to the next resolver in the chain. if (!canHandle(viewName, locale)) { return null ; } // Check for special "redirect:" prefix. if (viewName.startsWith(REDIRECT_URL_PREFIX)) { String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length()); RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible()); String[] hosts = getRedirectHosts(); if (hosts != null ) { view.setHosts(hosts); } return applyLifecycleMethods(REDIRECT_URL_PREFIX, view); // return "direct:/patch"在这里构造view } // Check for special "forward:" prefix. if (viewName.startsWith(FORWARD_URL_PREFIX)) { String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length()); InternalResourceView view = new InternalResourceView(forwardUrl); return applyLifecycleMethods(FORWARD_URL_PREFIX, view); // return "forward:/patch" 在这里构造view } // Else fall back to superclass implementation: calling loadView. return super .createView(viewName, locale); // return mav 在这里构造view } |
关于ViewResolver的代码执行顺序, 前面分析那么多,这里再打断点快速验证一下:
进DispatcherServlet的doDispatch看到就是这个解析器:
断点放在这里,
然后下一步:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持自学编程网。
- 本文固定链接: https://zxbcw.cn/post/198625/
- 转载请注明:必须在正文中标注并保留原文链接
- QQ群: PHP高手阵营官方总群(344148542)
- QQ群: Yii2.0开发(304864863)