spring mvc大致流程
- http请求到Controller
- Controller执行完成返回逻辑视图名(字符串)
- spring用ViewResolver解析逻辑视图名到具体的视图对象
- 具体的视图对象可以渲染成html输出到浏览器
如果要支持多种视图一种方法就是在ViewResolver动动手脚了。DispatcherServlet(spring mvc核心前端控制器)中解析视图的代码如下所示:
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
//迭代容器中所有的viewResolver
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) { //找到一个视图就返回了
return view;
}
}
return null;
}
这里就是迭代spring容器中所有的viewResolver,如果有一个viewResover解析出视图(view!=null),就用这个视图了。
spring对jsp,velocity等视图技术早就有相应的viewResolver支持,但是如果只是简单的都配置到容器中又不能和谐的工作。所以我们可以自己写一个viewResolver,来协调已有的这些viewResolver和谐的工作,那么依据什么来协调呢?可以根据视图的扩展名,就是说controller返回逻辑视图名的时候带上扩展名。我们自定义的viewResolver根据逻辑视图名的扩展名来协调返回那种视图技术来渲染。
自定义viewResolver如下
/**
* 根据逻辑视图名的扩展名来决定用那种视图(View)技术渲染
* Created by yanglikun on 2016/9/3.
*/
public class ViewNameExtensionViewResolver implements ViewResolver, Ordered {
private static final Log logger = LogFactory.getLog(ViewNameExtensionViewResolver.class);
private int order = 0;
Map<String, ViewResolver> viewResolvers = new HashMap<String, ViewResolver>();
public View resolveViewName(String viewName, Locale locale) throws Exception {
String viewExtension = StringUtils.getFilenameExtension(viewName);
if (StringUtils.isEmpty(viewExtension)) {
return null;
}
ViewResolver viewResolver = viewResolvers.get(viewExtension);
if (viewResolver != null) {
View view = viewResolver.resolveViewName(viewName, locale);
if (logger.isDebugEnabled()) {
logger.debug("Returning [" + view + "] based on view extension '"
+ viewExtension + "'");
}
return view;
}
if (logger.isWarnEnabled()) {
logger.warn("No view found with view extension[" + viewExtension + "]; returning null");
}
return null;
}
public void setViewResolvers(Map<String, ViewResolver> viewResolvers) {
this.viewResolvers.putAll(viewResolvers);
}
public Map<String, ViewResolver> getViewResolvers() {
return viewResolvers;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
}
这里不仅实现了ViewResolver接口,还实现了Ordered 接口,是因为,系统会配置多个viewresolver,spring容器会根据Ordered接口的getOrder返回的值把容器中所有的viewResolver进行排序,值越小,优先级越高。
xml的配置如下
其中internalResourceViewResolver和velocityViewResolver就是常规的resolver的配置了,详细的配置见
https://github.com/yanglikun/spring-mvc/blob/master/src/main/resources/spring/spring-config-mvc.xml
<bean class="org.btlas.framework.viewresolver.ViewNameExtensionViewResolver">
<property name="order" value="0"/>
<property name="viewResolvers">
<map>
<entry key="jsp" value-ref="internalResourceViewResolver"/>
<entry key="vm" value-ref="velocityViewResolver"/>
</map>
</property>
</bean>
这里我们把自己的viewResolver的order配置为0,这样spring在解析视图的时候也会提高一些效率,因为根据上面spring的源码,解析到view后,就不会迭代后面的viewResolver进行处理了。
示例代码:
clone 项目 https://github.com/yanglikun/spring-mvc.git
其中的org.btlas.controller.MultiViewController就是区分不同视图技术的