由于本例需要支持 SWF 及自定义后缀名的文件,所以我们期望能够根据不同请求的后缀名来调用不同的视图解析器。实际上,Spring 已经提供了类似的视图解析器-ContentNegotiatingViewResolver,它可以根据请求的文件后缀名或请求的 Accept 头来查找视图。ContentNegotiatingViewResolver 本身并不负责查找视图,它只是将视图查找工作代理给所注册的视图解析器,清单 3 给出了 ContentNegotiatingViewResolver 的配置文件片段。
清单 3.ContentNegotiatingViewResolver
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> <property name="mediaTypes"> <map> <entry key="atom" value="application/atom+xml"/> <entry key="html" value="text/html"/> <entry key="json" value="application/json"/> </map> </property> <property name="viewResolvers"> <list> <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </list> </property> <property name="defaultViews"> <list> <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" /> </list> </property> </bean>
从清单 3 可以发现,在使用 ContentNegotiatingViewResolver 时,一般需要配置三个部分:
- 注册所支持的媒体类型,也就是文件后缀名及其对应的文件内容类型。
- 视图解析器,也就是 ContentNegotiatingViewResolver 在查找视图时,实际所代理使用的视图解析器。
- 默认视图,当视图解析器没有查找到合适的视图时,将使用该默认视图。
关于 ContentNegotiatingViewResolver 的具体使用,读者可以参见 Spring 官方文档。
本例开发的复合视图解析器和 ContentNegotiatingViewResolver 类似,虽然本例也可以使用 ContentNegotiatingViewResolver 来实现相同的功能,但 ContentNegotiatingViewResolver 较为复杂,它在查找视图时,会将所有注册到 ContentNegotiatingViewResolver 下的视图解析器全部调用一遍,然后将所有查找到的视图保存为候选视图,最后再根据筛选条件,筛选出一个最为合适的视图。而本例中的复合视图解析器则简单实用的多,只需要注册文件后缀名和对应的视图解析器即可,当为请求找到相应的视图解析器时,便直接调用该视图解析器,而不需调用其所注册的所有视图解析器,清单 4 给出了在 Spring 配置文件中对该复合视图解析的配置文件片段。
清单 4. 复合视图解析器配置片段
<bean id="viewResolver" class="com.sample.web.viewresolver.MultipleViewResolver" p:order="0"> <property name="resolvers"> <map> <entry key="config"> <bean class="com.sample.web.viewresolver.GenericFileViewResolver" p:location="/WEB-INF/config/" p:cache="false"> <property name="viewName" value="configFileView"/> </bean> </entry> <entry key="swf"> <bean class="com.sample.web.viewresolver.GenericFileViewResolver" p:location="/WEB-INF/swf/" p:cache="false"> <property name="viewName" value="swfFileView"/> </bean> </entry> </map> </property> </bean>
在配置文件中,我们为不同的文件后缀名注册了相应的视图解析器,并为该视图解析器配置了所对应查找的视图类。同 ContentNegotiatingViewResolver 类似,本例中的复合视图解析器 MultipleViewResolver 也是将具体的视图查找工作代理给所注册的视图解析器,实际上,MultipleViewResolver 也是一个普通的视图解析器,不过在核心方法 loadView(如清单 5)中,首先获得请求视图的后缀名,然后根据后缀名获得所注册的视图解析器,最后,再使用获得的视图解析器查找视图。
清单 5 .MultipleViewResolver 代码片段
@Override protected View loadView(String viewName, Locale locale) throws Exception { String fileExtension = StringUtils.getFilenameExtension(viewName); // 返回 null 如果没有后缀名,以便下一个 resolver 处理 if (fileExtension == null) { return null; } // 通过后缀名获取 resolver ViewResolver resolver = resolvers.get(fileExtension); //return null to invoke next resolver if no resolver found return resolver == null ? null : resolver.resolveViewName(viewName, locale); }