第一节我们看到了ViewResolver对ModelAndView中属性Oject view为String时,将调用方法:
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); if (view != null) { return view; } } return null; }
所以这一节就来了解下resolveViewName的机制。
来看看我们第一眼能懂的,Internal产生的是JstlView,Freemarker不用说,Velocity不用说,那就从这三个类来入手。
还是过程式的代码阅读,我们去找resovlveViewName 这个方法。
在AbstractCachingViewResolver类中:
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
synchronized (this.viewCache) {
View view = this.viewCache.get(cacheKey);
if (view == null) {
// Ask the subclass to create the View object.
view = createView(viewName, locale);
this.viewCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
return view;
}
}
}
protected View createView(String viewName, Locale locale) throws Exception {
return loadView(viewName, locale);
}
protected abstract View loadView(String viewName, Locale locale) throws Exception;//子类必须实现这个方法
loadView方法又将我们带入了AbstractCachingViewResolver的子类UrlBasedViewResolver类中,可以发现UrlBasedViewResolver类中:
/** * Overridden to implement check for "redirect:" prefix. * <p>Not possible in <code>loadView</code>, since overridden * <code>loadView</code> versions in subclasses might rely on the * superclass always creating instances of the required view class. * @see #loadView * @see #requiredViewClass */ @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()); return new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible()); } // 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); }
这里千万别糊涂哦,这可是java基础知识多态哦。。。。
因为在DispatcherServlet中的viewResolvers保存的都是ViewResolver的实现类的对象,那么调用接口的resolveViewName方法后,就得一层层来看这个方法了,从代码中我们看到的是只有AbstractCachingViewResolver有这个方法,那肯定调用的是它的。但是在resolveViewName中又调用了createView,那么这里调用的就是UrlBasedViewResolver的方法了。
所以在该方法前面的if都没得到满足时,才去显示的调用父类的createView方法。
// Else fall back to superclass implementation: calling loadView. return super.createView(viewName, locale);
这时父类的createView调用了loadView方法,而UrlBasedViewResolver又实现了这一方法,自然而然是调用它的loadView方法,而Internal暴露了buildView方法,则loadView执行时首先调用Internal的buildView。
@Override protected View loadView(String viewName, Locale locale) throws Exception { AbstractUrlBasedView view = buildView(viewName); View result = (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName); return (view.checkResource(locale) ? result : null); }
UrlBasedViewResolver类中的buildView方法
protected AbstractUrlBasedView buildView(String viewName) throws Exception { AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass()); view.setUrl(getPrefix() + viewName + getSuffix()); String contentType = getContentType(); if (contentType != null) { view.setContentType(contentType); } view.setRequestContextAttribute(getRequestContextAttribute()); view.setAttributesMap(getAttributesMap()); return view; }
InternalResourceViewResolver的buildView方法,忽然看到这个viewClass让我眼前一亮,终于知道Freemarker去生成静态页面是怎么做到的了,用自己实现的类配置在Resolver的属性中,这样等于是做了一次拦截。
<bean id="freemarkerViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> <property name="contentType" value="text/html;charset=gbk" /> <property name="viewClass" value="com.sha0k.util.MyFreeMarkerView"> </property> <property name="exposeRequestAttributes"> <value>true</value> </property> <property name="exposeSessionAttributes"> <value>true</value> </property> <property name="order"> <value>1</value> </property> <property name="suffix" value=".ftl" /> </bean>
@Override protected AbstractUrlBasedView buildView(String viewName) throws Exception { InternalResourceView view = (InternalResourceView) super.buildView(viewName); if (this.alwaysInclude != null) { view.setAlwaysInclude(this.alwaysInclude); } if (this.exposeContextBeansAsAttributes != null) { view.setExposeContextBeansAsAttributes(this.exposeContextBeansAsAttributes); } if (this.exposedContextBeanNames != null) { view.setExposedContextBeanNames(this.exposedContextBeanNames); } view.setPreventDispatchLoop(true); return view; }