spring-mvc 版本4.04html
今天翻項目中freemarker相關代碼,疑惑springmvc是怎麼發現freemarker的,因而單步進去。
DispatcherServlet的doDispatch方法裏有這麼一句:java
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);web
能夠猜到,這是handler業務處理後,到渲染頁面的階段了,也就是freemarker該出場的時候了。spring
進入processDispatchResult這個方法,看到這麼一段spring-mvc
// Did the handler return a view to render? 返回一個view 去渲染 if (mv != null && !mv.wasCleared()) { render(mv, request, response);//就是渲染了 if (errorView) { WebUtils.clearErrorRequestAttributes(request); } }
而後render方法,看到緩存
View view; if (mv.isReference()) {//mv 是String類型的,好比url // We need to resolve the view name. view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);//獲取來渲染頁面的view if (view == null) { throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + getServletName() + "'"); } }
看到resolveViewName方法mvc
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception { for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); //建立根據viewName(其實就是controller裏返回的url,好比/wiew/test.ftl)建立view //那隨便一個url都能有相應的view嗎?固然不是。好比要先匹配,這個下面說, if (view != null) { return view; } } return null; }
原來view解決方案已經都存在viewResolvers對象裏了,那何時存的呢,代碼裏搜索下,找到以下方法:app
/** * Initialize the ViewResolvers used by this class. * <p>If no ViewResolver beans are defined in the BeanFactory for this * namespace, we default to InternalResourceViewResolver. */ private void initViewResolvers(ApplicationContext context) { this.viewResolvers = null; if (this.detectAllViewResolvers) {//boolean值,是否自動檢測全部的ViewResolver, // Find all ViewResolvers in the ApplicationContext, including ancestor contexts. //是自動檢測,就用BeanFactoryUtils的beansOfTypeIncludingAncestors方法,找全部的ViewResolver //能夠看到這個方法頗有用,在項目也能夠用,,可你找到全部ViewResolver.class類型或子類的bean,很好用。 //這也體現了mvc框架的v的部分。任何實現了ViewResolver接口的類,均可做爲視圖用 Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false); if (!matchingBeans.isEmpty()) { this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.values()); // We keep ViewResolvers in sorted order. OrderComparator.sort(this.viewResolvers); } } else { try { ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);//若是不自動檢測 //String VIEW_RESOLVER_BEAN_NAME = "viewResolver"; 就找名字爲viewResolver的bean做爲ViewResolver this.viewResolvers = Collections.singletonList(vr); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default ViewResolver later. } } // Ensure we have at least one ViewResolver, by registering // a default ViewResolver if no other resolvers are found. //以上都沒找到視圖,只有獲取一個默認的。 if (this.viewResolvers == null) { this.viewResolvers = getDefaultStrategies(context, ViewResolver.class); if (logger.isDebugEnabled()) { logger.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default"); } } }
initViewResolvers這個方法,是在onRefresh的調用的,實際上是重寫了父類的方法,
在spring的初始化時自動被調用。spring模板方法的神力開始起做用了。框架
protected void onRefresh(ApplicationContext context) { initStrategies(context); }
--------------------------------------------------------------------------
以上是spring-mvc怎麼發現第三方viewResolver(不限freemarker)的,而後看看,請求來的url怎麼找到匹配的view的,接着說上面遺留的問題
再看這句代碼:ide
View view = viewResolver.resolveViewName(viewName, locale);
這個方法是org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver父類AbstractCachingViewResolver實現的。
跟進去:
@Override 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.viewAccessCache.put(cacheKey, view); this.viewCreationCache.put(cacheKey, view); if (logger.isTraceEnabled()) { logger.trace("Cached view [" + cacheKey + "]"); } } } } } return (view != UNRESOLVED_VIEW ? view : null); } }
//因爲UrlBasedViewResolver類從新寫了這個方法因此,是UrlBasedViewResolver的createView方法 @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. //就在這裏,代碼首先檢查resolver是否支持這個viewName(url) 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()); return applyLifecycleMethods(viewName, view); } // Check for special "forward:" prefix. if (viewName.startsWith(FORWARD_URL_PREFIX)) { String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length()); return new InternalResourceView(forwardUrl); } // Else fall back to superclass implementation: calling loadView. return super.createView(viewName, locale); } //在這裏 protected boolean canHandle(String viewName, Locale locale) { String[] viewNames = getViewNames(); //viewNames就是在xml文件裏配置的如,freemarker //<property name="viewNames"> //<array> //<value>*.ftl</value> //<value>*.html</value> //</array> //</property> return (viewNames == null || PatternMatchUtils.simpleMatch(viewNames, viewName));//作模式匹配 }
最後類圖,注意方法的重寫。子類老是調用最近上級節點方法。