第一节我们看到了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;
}