- ViewResolverComposite:这是一个组合的视图解析器,届时可以用来代理其他具体干活的视图解析器。
接下来我们就对这四个视图解析器逐一进行介绍,先从最简单的 BeanNameViewResolver 开始吧。
BeanNameViewResolver 的处理方式非常简单粗暴,直接根据 viewName 去 Spring 容器中查找相应的 Bean 并返回,如下:
@Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws BeansException {
ApplicationContext context = obtainApplicationContext();
if (!context.containsBean(viewName)) {
return null;
}
if (!context.isTypeMatch(viewName, View.class)) {
return null;
}
return context.getBean(viewName, View.class);
}
先去判断下有没有相应的 Bean,然后再检查下 Bean 的类型对不对,都没问题,直接查找返回即可。
3.ContentNegotiatingViewResolver
ContentNegotiatingViewResolver 其实是目前广泛使用的一个视图解析器,主要是添加了对 MediaType 的支持。ContentNegotiatingViewResolver 这个是 Spring3.0 中引入的的视图解析器,它不负责具体的视图解析,而是根据当前请求的 MIME 类型,从上下文中选择一个合适的视图解析器,并将请求工作委托给它。
这里我们就先来看看 ContentNegotiatingViewResolver#resolveViewName 方法:
public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
List requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
if (requestedMediaTypes != null) {
List candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
if (bestView != null) {
return bestView;
}
}
if (this.useNotAcceptableStatusCode) {
return NOT_ACCEPTABLE_VIEW;
}
else {
return null;
}
}
这里的代码逻辑也比较简单:
-
首先是获取到当前的请求对象,可以直接从 RequestContextHolder 中获取。然后从当前请求对象中提取出 MediaType。
-
如果 MediaType 不为 null,则根据 MediaType,找到合适的视图解析器,并将解析出来的 View 返回。
-
如果 MediaType 为 null,则为两种情况,如果 useNotAcceptableStatusCode 为 true,则返回 NOT_ACCEPTABLE_VIEW 视图,这个视图其实是一个 406 响应,表示客户端错误,服务器端无法提供与 Accept-Charset 以及 Accept-Language 消息头指定的值相匹配的响应;如果 useNotAcceptableStatusCode 为 false,则返回 null。
现在问题的核心其实就变成 getCandidateViews 方法和 getBestView 方法了,看名字就知道,前者是获取所有的候选 View,后者则是从这些候选 View 中选择一个最佳的 View,我们一个一个来看。
先来看 getCandidateViews:
private List getCandidateViews(String viewName, Locale locale, List requestedMediaTypes)
throws Exception {
List candidateViews = new ArrayList<>();
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
candidateViews.add(view);
}
for (MediaType requestedMediaType : requestedMediaTypes) {
List extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);
for (String extension : extensions) {
String viewNameWithExtension = viewName + ‘.’ + extension;
view = viewResolver.resolveViewName(viewNameWithExtension, locale);
if (view != null) {