Spring mvc 使用多种viewResolver的问题

问题大概是这样的:我配置了两个viewResovler:
[code="java"]






utf-8

utf-8










text/html;charset=UTF-8

0





text/html;charset=UTF-8
1

[/code]
目的是为了,请求多种视图,viewResovler的order如上图使用
[code="java"]
@RequestMapping(value ="/welcome/user")
public ModelAndView welcome(HttpServletRequest request,HttpServletResponse response,
ModelMap modelMap) {
return new ModelAndView("welcome",modelMap);
//普通转向
}
[/code]
第一次http://localhost:8080/Prj/welcome/user 请求/WEB-INF/velo/目录底下的welcome.vm文件可以正常访问
当我把java代码修改成这样
[code="java"]
@RequestMapping(value ="/welcome/user")
public ModelAndView welcome(HttpServletRequest request,HttpServletResponse response,
ModelMap modelMap) {
return new ModelAndView("welcome",modelMap);
//普通转向
}
[/code]
并且把jspresovler的order改成0,把velocity的resovler改成1
目的就是为了在/WEB-INF/jsp/目录下找不到welcome.jsp文件,按理说servlet会继续在另外一个resovler里面来查找.vm文件
应该是去找 /WEB-INF/veclo/目录底下寻找 welcome.vm文件,但是
当我请求上面的请求的时候,tomcat提示404错误,后台打印 说找不到welcome.jsp文件
是什么原因?
各位大虾?

补充:当我把两个order都设为0的时候,也是可以找到的

[code="java"]public boolean checkResource(Locale locale) throws Exception {
return true;
}[/code]
罪魁祸首 就是spring实现该方法时返回true,这样相当于view总是存在,从而导致其余视图解析器无法得到解析机会。

覆盖该方法,应该就可以了。

[code="java"]public class IcomJstlView extends JstlView {

public boolean checkResource(Locale locale) throws Exception {
    File file = new File(this.getServletContext().getRealPath("/")+getUrl());
    return file.exists();//判断该jsp页面是否存在
}

}[/code]

我没有用过两个视图,所以看了一下源码,看代码:
[code="java"]
DispatchServlet.java中 doDispatch最后是调用render方法做视图渲染的:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {

// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);

View view = null;

if (mv.isReference()) {
    // We need to resolve the view name.
    view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
    if (view == null) {
        throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                "' in servlet with name '" + getServletName() + "'");
    }
}
else {
    // No need to lookup: the ModelAndView object contains the actual View object.
    view = mv.getView();
    if (view == null) {
        throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                "View object in servlet with name '" + getServletName() + "'");
    }
}

// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
    logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
view.render(mv.getModelInternal(), request, response);

}

mv.isReference() 是说你是不是用名字代替传递一个View实例给ModelAndView对象,你传递的都是名字,所以再看
protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)
throws Exception {

for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
    ViewResolver viewResolver = (ViewResolver) it.next();
    View view = viewResolver.resolveViewName(viewName, locale);
    if (view != null) {
        return view;
    }
}
return null;

}

this.viewResolvers 这个就是你配的两个viewResolvers,分别试着去创建View,再看看创建View的代码
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
logger.warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
synchronized (this.viewCache) {
View view = (View) this.viewCache.get(cacheKey);
if (view == null) {
// Ask the subclass to create the View object.
view = createView(viewName, locale);
this.viewCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
return view;
}
}
}
最终落实到 createView,因为你的两个viewResolvers都是实现的 UrlBasedViewResolver,所以看UrlBasedViewResolver这里面的buildView方法就行了
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
view.setUrl(getPrefix() + viewName + getSuffix());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
return view;
}
并没有看到有试图去找(getPrefix() + viewName + getSuffix())对应的文件,所以并不会返回null,所以只查找了jsp就没有查找fm了

Spring并不会去检查你这个文件(welcome.jsp)是否存在,只是最后渲染的时候交给给自处理 VelocityViewResolver交给Velocity处理,
InternalResourceViewResolver交给 servlet request 处理

也可以说UrlBasedViewResolver找出来的View没有被Spring容器管理,所以他不知道有没有

如果想实现你的功能,可以扩展DispatchServlet的resolveViewName方法,例如:
class MyDispatcherServlet extends DispatcherServlet {
private List viewResolvers;

@Override
protected View resolveViewName(String viewName, Map model, Locale locale,
        HttpServletRequest request) throws Exception {
    WebApplicationContext context = getWebApplicationContext();
    for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
        ViewResolver viewResolver = (ViewResolver) it.next();
        View view = viewResolver.resolveViewName(viewName, locale);
        if (view != null) {

            if (view instanceof AbstractUrlBasedView) {
                try {
                    Resource resource = context.getResource(((AbstractUrlBasedView) view).getUrl());
                    if (resource == null) {
                        // log
                        continue;
                    }
                } catch (Exception e) {
                    // log
                    continue;
                }
            }

            return view;
        }
    }
    return null;
}

}
当然你要初始化viewResolvers 因为DispatchServlet中该属性为私有的
[/code]

网上找到的:
如果为DispatcherServlet指定多个ViewResolver的话,不要给予InternalResour- ceViewResolver以及其他UrlBasedViewResolver子类过高的优先级,因为这些ViewResolver即使找不到相应的视图,也不会返回null以给我们轮询下一个ViewResolver的机会,这样,我们所指定的其他ViewResolver实际上就形同虚设。合理的处理方式是,给予ResourceBundleView- Resolver或者XmlViewResolver这种能够通过返回null以表明无法找到相应视图的ViewResolver较高的优先级,而只是将InternalResourceViewResolver(或者其他类似行为的ViewResolver)添加为最低优先级ViewResolver,以作为DispatcherServlet的后备查找对象。

不错