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));//作模式匹配 }
最后类图,注意方法的重写。子类老是调用最近上级节点方法。