ContentNegotiatingViewResolver视图解析器是Spring MVC 中常用的一个视图解析器。
这个实现了ViewResolver接口,基于请求文件和Accept 头部信息。ContentNagotiatingViewResolver自己并不解析视图,而是委派给其他的视图处理器。 回过头来,哪些其他的视图解析器
是自动地从应用上下文中挑选出来的,虽然它可以通过viewResolvers属性配置。为了使这个解析器正常工作,序号需要设置成比其他的视图处理器高的优先级。这个视图处理器使用必要的媒体类型为一个请求去选择一个合适的视图。媒体类型通过如下的标准决定:
如果请求路径中包含一个文件扩展名或者将favorPathExtension属性置为true, mediaTypes属性是否匹配媒体类型。
看看这个类的属性:
// 是否使用请求文件扩展名作为媒体类型 private boolean favorPathExtension = true; // 是否使用请求参数决定媒体类型 private boolean favorParameter = false; // 媒体类型参数名 private String parameterName = "format"; private boolean useNotAcceptableStatusCode = false; // 是否忽略Accept header private boolean ignoreAcceptHeader = false; private boolean useJaf = jafPresent; // 媒体类型MAP private ConcurrentMap<String, MediaType> mediaTypes = new ConcurrentHashMap<String, MediaType>(); // 默认的视图 private List<View> defaultViews; //默认的媒体类型 private MediaType defaultContentType; // 视图解析器 private List<ViewResolver> viewResolvers;
默认的优先级是最高的,也就是最小整数值!优先级和整数值是反比关系,数值越小优先级越高。
视图解析器的初始化,首先是从上下文中去查找,其次获取配置的解析器,最后通过解析器的优先级进行排序。
@Override protected void initServletContext(ServletContext servletContext) { Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(getApplicationContext(), ViewResolver.class).values(); if (this.viewResolvers == null) { this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.size()); for (ViewResolver viewResolver : matchingBeans) { if (this != viewResolver) { this.viewResolvers.add(viewResolver); } } } else { for (int i=0; i < viewResolvers.size(); i++) { if (matchingBeans.contains(viewResolvers.get(i))) { continue; } String name = viewResolvers.get(i).getClass().getName() + i; getApplicationContext().getAutowireCapableBeanFactory().initializeBean(viewResolvers.get(i), name); } } if (this.viewResolvers.isEmpty()) { logger.warn("Did not find any ViewResolvers to delegate to; please configure them using the " + "'viewResolvers' property on the ContentNegotiatingViewResolver"); } OrderComparator.sort(this.viewResolvers); }
视图解析器的入口,首先通过请求路径获取媒体类型mediaType
public View resolveViewName(String viewName, Locale locale) throws Exception { RequestAttributes attrs = RequestContextHolder.getRequestAttributes(); Assert.isInstanceOf(ServletRequestAttributes.class, attrs); List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest()); if (requestedMediaTypes != null) { List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes); View bestView = getBestView(candidateViews, requestedMediaTypes); if (bestView != null) { return bestView; } } if (this.useNotAcceptableStatusCode) { if (logger.isDebugEnabled()) { logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code"); } return NOT_ACCEPTABLE_VIEW; } else { logger.debug("No acceptable view found; returning null"); return null; } }
通过所有视图解析器去解析视图,将解析的视图放入一个待选的集合中,然后在通过追加媒体类型的后缀再次去解析一次,将结果放入待选视图集合中,如果最后的集合还是空的则放入默认的视图集。
private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception { List<View> candidateViews = new ArrayList<View>(); for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { candidateViews.add(view); } for (MediaType requestedMediaType : requestedMediaTypes) { List<String> extensions = getExtensionsForMediaType(requestedMediaType); for (String extension : extensions) { String viewNameWithExtension = viewName + "." + extension; view = viewResolver.resolveViewName(viewNameWithExtension, locale); if (view != null) { candidateViews.add(view); } } } } if (!CollectionUtils.isEmpty(this.defaultViews)) { candidateViews.addAll(this.defaultViews); } return candidateViews; }
最后从这些视图中返回一个最优的视图, 使用volecity 框架最常见的就是 xxx.htm.vm 或xxx.vm.htm 视图找不到 这样的一个错误消息。
为了解决命中率不高的问题,可以使用通过扩展名决定使用哪一个ViewResolver,根据ContentNegotiatingViewResolver的设计思路,设计一个ViewResolver ,它本身布解析视图,而是委派给指定的相应的视图解析器。
/** * 根据请求的扩展名决定使用什么ViewResolver * @author zhangwei_david * @version $Id: ExtensionViewResolver.java, v 0.1 2014年12月2日 上午10:37:02 zhangwei_david Exp $ */ public class ExtensionViewResolver implements ViewResolver, Ordered { private static final Logger logger = LogManager .getLogger(ExtensionViewResolver.class); private HashMap<String, ViewResolver> resolvers = new HashMap<String, ViewResolver>(); private ViewResolver defaultViewReslver; private UrlPathHelper urlPathHelper = new UrlPathHelper(); /** * 默认使用最高优先级 */ private int order = Ordered.HIGHEST_PRECEDENCE; /** * @see org.springframework.core.Ordered#getOrder() */ public int getOrder() { return order; } /** * @see org.springframework.web.servlet.ViewResolver#resolveViewName(java.lang.String, java.util.Locale) */ public View resolveViewName(String viewName, Locale locale) throws Exception { String extension = getExtension(viewName); if (logger.isDebugEnabled()) { logger.debug("当前的扩展参数是:" + extension); } ViewResolver resolver = getResolver(extension); if (logger.isInfoEnabled()) { logger.info("当前的视图解析器是:" + ToStringBuilder.reflectionToString(resolver)); } return resolver.resolveViewName(viewName, locale); } /** * * @param viewName * @return */ private String getExtension(String viewName) { RequestAttributes attrs = RequestContextHolder.getRequestAttributes(); Assert.isInstanceOf(ServletRequestAttributes.class, attrs); HttpServletRequest request = ((ServletRequestAttributes) attrs).getRequest(); String uri = urlPathHelper.getRequestUri(request); return StringUtils.getFilenameExtension(uri); } /** * * @param extension * @return */ private ViewResolver getResolver(String extension) { ViewResolver resolver = resolvers.get(extension); return resolver == null ? defaultViewReslver : resolver; } /** * Setter method for property <tt>resolvers</tt>. * * @param resolvers value to be assigned to property resolvers */ public void setResolvers(HashMap<String, ViewResolver> resolvers) { this.resolvers = resolvers; } /** * Setter method for property <tt>order</tt>. * * @param order value to be assigned to property order */ public void setOrder(int order) { this.order = order; } /** * Getter method for property <tt>defaultViewReslver</tt>. * * @return property value of defaultViewReslver */ public ViewResolver getDefaultViewReslver() { return defaultViewReslver; } /** * Setter method for property <tt>defaultViewReslver</tt>. * * @param defaultViewReslver value to be assigned to property defaultViewReslver */ public void setDefaultViewReslver(ViewResolver defaultViewReslver) { this.defaultViewReslver = defaultViewReslver; } }
定义一个JSON的视图解析器:
/** * * @author zhangwei_david * @version $Id: JsonViewResolver.java, v 0.1 2014年11月30日 上午12:09:44 zhangwei_david Exp $ */ public class JsonViewResolver implements ViewResolver, Ordered { private static final Logger logger = LogManager.getLogger(JsonViewResolver.class); private int order = Ordered.HIGHEST_PRECEDENCE; private UrlPathHelper urlPathHelper; /** * @see org.springframework.web.servlet.ViewResolver#resolveViewName(java.lang.String, java.util.Locale) */ public View resolveViewName(String viewName, Locale locale) throws Exception { RequestAttributes attrs = RequestContextHolder.getRequestAttributes(); Assert.isInstanceOf(ServletRequestAttributes.class, attrs); HttpServletRequest request = ((ServletRequestAttributes) attrs).getRequest(); String uri = urlPathHelper.getRequestUri(request); if (uri.contains(".json")) { LogUtils.info(logger, "Handler the Uri {0} and Return A JSON View", uri); return new MappingJacksonJsonView(); } // return null; } /** * Getter method for property <tt>urlPathHelper</tt>. * * @return property value of urlPathHelper */ public UrlPathHelper getUrlPathHelper() { return urlPathHelper; } /** * Setter method for property <tt>urlPathHelper</tt>. * * @param urlPathHelper value to be assigned to property urlPathHelper */ public void setUrlPathHelper(UrlPathHelper urlPathHelper) { this.urlPathHelper = urlPathHelper; } /** * @see org.springframework.core.Ordered#getOrder() */ public int getOrder() { return order; } /** * Setter method for property <tt>order</tt>. * * @param order value to be assigned to property order */ public void setOrder(int order) { this.order = order; } }
<bean id="urlPathHelper " class="org.springframework.web.util.UrlPathHelper" /> <bean id="jsonViewResolver" class="com.david.web.view.JsonViewResolver"> <property name="order" value="1" /> <property name="urlPathHelper" ref="urlPathHelper" /> </bean> <bean id="velocityViewResolver" class="org.springframework.web.servlet.view.velocity.VelocityLayoutViewResolver"> <property name="order" value="3" /> <property name="viewClass" value="org.springframework.web.servlet.view.velocity.VelocityLayoutView" /> <property name="cache" value="false" /> <property name="layoutUrl" value="layout.vm" /> <property name="contentType"> <value>text/html;charset=UTF-8</value> </property> <property name="exposeSpringMacroHelpers" value="true" /> </bean> <bean class="com.david.web.view.ExtensionViewResolver"> <property name="defaultViewReslver" ref="velocityViewResolver" /> <property name="resolvers"> <map> <entry key="htm" value-ref="velocityViewResolver" /> <entry key="json" value-ref="jsonViewResolver" /> </map> </property> </bean>