spring源码解析-web系列(一):启动
spring源码解析-web系列(二):处理请求的过程
spring源码解析-web系列(三):九大组件之HandlerMapping
spring源码解析-web系列(四):九大组件之HandlerAdapter
spring源码解析-web系列(五):解析请求参数
spring源码解析-web系列(六):九大组件之ViewResolver
spring源码解析-web系列(七):九大组件之HandlerExceptionResolver
转载请标明出处:
https://blog.csdn.net/bingospunky/article/details/98622667
本文出自马彬彬的博客
ViewResolver的作用是通过ViewName获取到View,从而可以渲染结果。ViewResolver的接口定义如下:
代码1 (org.springframework.web.servlet.ViewResolver):
public interface ViewResolver {
View resolveViewName(String var1, Locale var2) throws Exception;
}
ViewResolver的子类可以分为如下四类:
1.AbstractCachingViewResolver:提供了抽象缓存的能力,当缓存里不存在时,通过模板方法让子类创建。
2.BeanNameViewResolver:通过name去BeanFactory获取对象。简单至极,不予描述。
3.ContentNegotiatingViewResolver:内部分装了一些ViewResolver,具体工作通过内部封装的ViewResolver获取View,当获取到多个View时,该类通过MediaTypes(Content-type)选出一个最合适的View。
4.ViewResolverComposite:内部封装了一些ViewResolver,自己不干活,通过内部的ViewResolver干活。
代码2 (org.springframework.web.servlet.view.AbstractCachingViewResolver.resolveViewName):
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!this.isCache()) {
return this.createView(viewName, locale);
} else {
Object cacheKey = this.getCacheKey(viewName, locale);
View view = (View)this.viewAccessCache.get(cacheKey);
if (view == null) {
Map var5 = this.viewCreationCache;
synchronized(this.viewCreationCache) {
view = (View)this.viewCreationCache.get(cacheKey);
if (view == null) {
view = this.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 (this.logger.isTraceEnabled()) {
this.logger.trace("Cached view [" + cacheKey + "]");
}
}
}
}
}
return view != UNRESOLVED_VIEW ? view : null;
}
}
代码2主要就是对缓存的操作,如果缓存里有,直接从缓存里取;如果缓存里没有,那么通过代码2第12行的createView方法创建,createView又调用了模板方法loadView让子类创建。
这里使用了两个Map做缓存,viewAccessCache是ConcurrentHashMap类型的,主要用来做检索缓存,线程安全的且效率高;viewCreationCache是LinkedHashMap类型的,主要用来控制缓存数量限制,如果做线程安全,它的效率不如ConcurrentHashMap,所以存在了LinkedHashMap的基础上又使用了ConcurrentHashMap。
UrlBasedViewResolver继承AbstractCachingViewResolver,实现createView方法和loadView方法来创建具体的View。
代码3 (org.springframework.web.servlet.view.UrlBasedViewResolver.createView):
protected View createView(String viewName, Locale locale) throws Exception {
if (!this.canHandle(viewName, locale)) {
return null;
} else {
String forwardUrl;
if (viewName.startsWith("redirect:")) {
forwardUrl = viewName.substring("redirect:".length());
RedirectView view = new RedirectView(forwardUrl, this.isRedirectContextRelative(), this.isRedirectHttp10Compatible());
return this.applyLifecycleMethods(viewName, view);
} else if (viewName.startsWith("forward:")) {
forwardUrl = viewName.substring("forward:".length());
return new InternalResourceView(forwardUrl);
} else {
return super.createView(viewName, locale);
}
}
}
代码3 UrlBasedViewResolver重写父类的createView,对 redirect 和 forward 类型的ViewName进行了处理,创建了对应的View返回。否则还是调用父类的createView方法进行处理。
代码4 (org.springframework.web.servlet.view.UrlBasedViewResolver):
protected View loadView(String viewName, Locale locale) throws Exception {
AbstractUrlBasedView view = this.buildView(viewName);
View result = this.applyLifecycleMethods(viewName, view);
return view.checkResource(locale) ? result : null;
}
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
AbstractUrlBasedView view = (AbstractUrlBasedView)BeanUtils.instantiateClass(this.getViewClass());
view.setUrl(this.getPrefix() + viewName + this.getSuffix());
String contentType = this.getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(this.getRequestContextAttribute());
view.setAttributesMap(this.getAttributesMap());
Boolean exposePathVariables = this.getExposePathVariables();
if (exposePathVariables != null) {
view.setExposePathVariables(exposePathVariables);
}
Boolean exposeContextBeansAsAttributes = this.getExposeContextBeansAsAttributes();
if (exposeContextBeansAsAttributes != null) {
view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
}
String[] exposedContextBeanNames = this.getExposedContextBeanNames();
if (exposedContextBeanNames != null) {
view.setExposedContextBeanNames(exposedContextBeanNames);
}
return view;
}
代码4的loadView方法是父类的模板方法,用来创建具体和View。代码4第2行调用了this.buildView创建AbstractUrlBasedView。代码4第7行根据该ViewResolver里保存的ViewClass创建对象,代码4第8~第29行对这个对象设置一些参数。
至此,创建View的代码已经在UrlBasedViewResolver类中了,那么它的子类主要做的就是如下两件事情了:1.使用setViewClass设置对应的View类型(重写requiredViewClass对View’类型的限制)。2.给创建出来的View设置属性。
代码5 (org.springframework.web.servlet.view.InternalResourceViewResolver):
public class InternalResourceViewResolver extends UrlBasedViewResolver {
private static final boolean jstlPresent = ClassUtils.isPresent("javax.servlet.jsp.jstl.core.Config", InternalResourceViewResolver.class.getClassLoader());
private Boolean alwaysInclude;
public InternalResourceViewResolver() {
Class<?> viewClass = this.requiredViewClass();
if (viewClass.equals(InternalResourceView.class) && jstlPresent) {
viewClass = JstlView.class;
}
this.setViewClass(viewClass);
}
protected Class<?> requiredViewClass() {
return InternalResourceView.class;
}
public void setAlwaysInclude(boolean alwaysInclude) {
this.alwaysInclude = alwaysInclude;
}
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
InternalResourceView view = (InternalResourceView)super.buildView(viewName);
if (this.alwaysInclude != null) {
view.setAlwaysInclude(this.alwaysInclude);
}
view.setPreventDispatchLoop(true);
return view;
}
}
代码5第6第11行,在InternalResourceViewResolver的构造方法中,设置了ViewClass。代码5第23第29行代码中创建InternalResourceView对象,并且设置一些参数。
ViewResolver的作用是通过ViewName获取到View,从而可以渲染结果。主要有AbstractCachingViewResolver、BeanNameViewResolver、ContentNegotiatingViewResolver、ViewResolverComposite,本文只描述了AbstractCachingViewResolver这个系列。
注意并不是每一个请求都会使用ViewResolver去获取View。我在 spring源码解析-web系列(四):九大组件之HandlerAdapter 这篇博客的ServletInvocableHandlerMethod这部分有提到使用returnValueHandler来处理返回值,并根据情况去渲染。如果是returnValueHandler没有渲染的,才会使用ViewResolver获取View来渲染;如果已经被returnValueHandler渲染过,那么不会再使用ViewResolver处理。